【Java】連接池、線程池 各種池【轉+整理】與享元模式。


談談連接池、線程池技術原理

 
 
  做互聯網研發,最早接觸使用jdbc技術,為了數據庫連接能夠復用,會用到c3p0、dbcp等數據庫連接池。應該是研發人員最早接觸的數據庫連接池,再到httpclient http連接池,再到微服務netty連接池redis客戶端連接池,以及jdk中線程池技術。

   這么多數據庫、http、netty連接池,jdk線程池,本質上都是連接池技術,連接池技術核心連接或者說創建的資源復用

   連接池技術核心:通過減少連接創建、關閉來提升性能。用於用戶后續使用,好處是后續使用不用在創建連接以及線程,因為這些都需要相關很多文件、連接資源、操作系統內核資源支持來完成構建,會消耗大量資源,並且創建、關閉會消耗應用程序大量性能。

   網絡連接本身會消耗大量內核資源,在linux系統下,網絡連接創建本身tcp/ip協議棧在內核里面,連接創建關閉會消耗大量文件句柄(linux中萬物皆文件,一種厲害抽象手段)系統資源。當下更多是應用tcp技術完成網絡傳輸,反復打開關閉,需要操作系統維護大量tcp協議棧狀態。

   連接池本質上是構建一個容器,容器來存儲創建好的線程、http連接、數據庫連接、netty連接等。對於使用方相當於黑盒,按照接口進行使用就可以了。各個連接池構建、使用管理詳細過程大概分成以下三部分。

   第一部分:首先初始化連接池,根據設置相應參數,連接池大小、核心線程數、核心連接數等參數,初始化創建數據庫、http、netty連接以及jdk線程。

   第二部分:連接池使用,前邊初始化好的連接池、線程池,直接從連接池、線程中取出資源即可進行使用,使用完后要記得交還連接池、線程池,通過池容器來對資源進行管理。

   第三部分:對於連接池維護,連接池、線程池來維護連接、線程狀態,不可用連接、線程進行銷毀,正在使用連接、線程進行狀態標注,連接、線程不夠后並且少於設置最大連接、線程數,要進行新連接、線程創建。

   通過上邊可以了解到各種連接池技術以及線程池原理或者說套路,理解原理才能不被紛繁復雜表象掩蓋。

   下面談談構建自己連接池,其實理解了連接池、線程原理,可以使用ArrayList來構建自己連接池、線程池。初始化時創建配置連接數、線程,存儲在ArrayList容器中,使用時從ArrayList從取出連接、線程進行使用,執行完任務后,提交回ArrayList容器。前提條件是單線程,在多線程狀態下要用線程安全容器

   前邊根據原理介紹了一個簡單連接池、線程池怎樣構建,實際工業級別線程池還要考慮到連接狀態,短連接重連,線程池維護管理高效,線程池穩定等多個因素。

   需要用到連接池而又沒有相關開源產品可用時,java連接池可以使用common-pool2來構建,比如google開源gRPC技術,本身是高性能跨平台技術,但目前作為微服務使用,沒有連接池、負載均衡等相應配套,這時可以根據需要自己基於Java容器構建自己連接池。也可以利用common-pool2構建連接池來提升應用性能,以及保持高可用。common-pool2本身不僅僅可以構建連接池使用,還可以用來構建對象池。

   連接池還有一個副作用就是實現了高可用,在微服務場景下一個連接不可用,那么再從netty連接池中取出一個進行使用,避免了連接不可用問題。

   掌握原理從比較全面掌握各種池技術,避免數據庫連接池,再到httpclient http連接池,再到微服務netty連接池,redis客戶端連接池,以及jdk中線程池,對象池各種各樣池技術,使我們眼花繚亂,花費過多時間,掌握原理機制以不變應萬變

   推廣一下這個方法,其他技術也是類似,深入掌握其原理,就可以明白其他類似技術相似原理,避免疲於應對各種新技術。但每一種架構設計與實現又與領域有着關系,也不可講原理不顧實際情況擴展。理論與架構設計、源碼學習相結合才是最好的,希望有幫助。

 


 享元模式的主要目的是實現對象的共享,即共享池,當系統中對象多的時候可以減少內存的開銷,通常與工廠模式一起使用。

FlyWeightFactory負責創建和管理享元單元,當一個客戶端請求時,工廠需要檢查當前對象池中是否有符合條件的對象,如果有,就返回已經存在的對象,如果沒有,則創建一個新對象,FlyWeight是超類。

一提到共享池,我們很容易聯想到Java里面的JDBC連接池,想想每個連接的特點,我們不難總結出:適用於作共享的一些個對象,他們有一些共有的屬性,就拿數據庫連接池來說,urldriverClassNameusernamepassworddbname這些屬性對於每個連接來說都是一樣的,所以就適合用享元模式來處理,建一個工廠類,將上述類似屬性作為內部數據其它的作為外部數據,在方法調用時,當做參數傳進來,這樣就節省了空間,減少了實例的數量。

看個例子:

 

看下數據庫連接池的代碼:

[java] view plaincopy

  1. public class ConnectionPool {  
  2.       
  3.     private Vector<Connection> pool;  
  4.       
  5.     /*公有屬性*/  
  6.     private String url = "jdbc:mysql://localhost:3306/test";  
  7.     private String username = "root";  
  8.     private String password = "root";  
  9.     private String driverClassName = "com.mysql.jdbc.Driver";  
  10.   
  11.     private int poolSize = 100;  
  12.     private static ConnectionPool instance = null;  
  13.     Connection conn = null;  
  14.   
  15.     /*構造方法,做一些初始化工作*/  
  16.     private ConnectionPool() {  
  17.         pool = new Vector<Connection>(poolSize);  
  18.   
  19.         for (int i = 0; i < poolSize; i++) {  
  20.             try {  
  21.                 Class.forName(driverClassName);  
  22.                 conn = DriverManager.getConnection(url, username, password);  
  23.                 pool.add(conn);  
  24.             } catch (ClassNotFoundException e) {  
  25.                 e.printStackTrace();  
  26.             } catch (SQLException e) {  
  27.                 e.printStackTrace();  
  28.             }  
  29.         }  
  30.     }  
  31.   
  32.     /* 返回連接到連接池 */  
  33.     public synchronized void release() {  
  34.         pool.add(conn);  
  35.     }  
  36.   
  37.     /* 返回連接池中的一個數據庫連接 */  
  38.     public synchronized Connection getConnection() {  
  39.         if (pool.size() > 0) {  
  40.             Connection conn = pool.get(0);  
  41.             pool.remove(conn);  
  42.             return conn;  
  43.         } else {  
  44.             return null;  
  45.         }  
  46.     }  
  47. }  

通過連接池的管理,實現了數據庫連接的共享,不需要每一次都重新創建連接,節省了數據庫重新創建的開銷,提升了系統的性能!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM