apache commons pool

apache commons下的pool

其中的borrowObject函數源代碼顯示其產生可用對象的過程:

    若是stack中有空閒的對象,則pop對象,激活對象(activate函數),驗證對象(validate函數)。最終將合格的對象返回給client。sql

                       若對象在這個流程中出錯,則在從stack中取出一個,並執行相同的流程。如此循環,直到stack爲空。數據庫

    若是stack爲空,則直接調用makeObject函數建立一個對象。在返回對象以前,還會調用驗證函數(validate)驗證是否有效。apache

轉載:

Apache common-pool, common-dbcp源碼解讀與對象池原理剖析 【轉載】

博客分類:

最近在作一個內部測試工具類的優化工做中接觸到了鏈接池, 對象池技術, 將原有的未使用鏈接池的數據庫訪問操做改爲鏈接池方式.性能有了很是大的提高, 事實證實, 通過兩次改造, 原來一個比較大的測試類須要500多秒, 第一次優化後只須要300多秒, 第二次改用鏈接池以後同一個測試類只須要80多秒.下面是改造過程當中的一些總結.
對象池就是以"空間換時間"的一種經常使用緩存機制, 這裏的"時間"特指建立時間,所以這也給出了對象池的適用範圍:若是一種對象的建立過程很是耗時的話, 那麼請使用對象池. 內部原理簡單的說, 就是將建立的對象放到一個容器中, 用完以後不是銷燬而是再放回該容器, 讓其餘的對象調用, 對象池中還涉及到一些高級的技術, 好比過時銷燬, 被破壞時銷燬, 對象數超過池大小銷燬, 對象池中沒有可用空閒對象時等待等等.

apache的common-pool工具庫是對池化技術原理的一種具體實現. 在闡述原來以前, 這裏先理解幾個概念: 
對象池(ObjectPool接口): 能夠把它認爲是一種容器, 它是用來裝池對象的, 而且包含了用來建立池對象的工廠對象 
池對象:就是要放到池容器中的對象, 理論上能夠是任何對象. 
對象池工廠(ObjectPoolFactory接口):用來建立對象池的工廠, 這個沒什麼好說的. 
池對象工廠(PoolableObjectFactory接口):用來建立池對象, 將不用的池對象進行鈍化(passivateObject), 對要使用的池對象進行激活(activeObject), 對池對象進行驗證(validateObject), 對有問題的池對象進行銷燬(destroyObject)等工做

對象池中封裝了建立, 獲取, 歸還, 銷燬池對象的職責, 固然這些工做都是經過池對象工廠來實施的, 容器內部還有一個或多個用來盛池對象的容器.對象池會對容器大小, 存放時間, 訪問等待時間, 空閒時間等等進行一些控制, 由於能夠根據須要來調整這些設置.

當須要拿一個池對象的時候, 就從容器中取出一個, 若是容器中沒有的話, 並且又沒有達到容器的最大限制, 那麼就調用池對象工廠, 新建一個池對象, 並調用工廠的激活方法, 對建立的對象進行激活, 驗證等一系列操做. 若是已經達到池容器的最大值, 而對象池中又經沒有空閒的對象, 那麼將會繼續等待, 直到有新的空閒的對象被丟進來, 固然這個等待也是有限度的, 若是超出了這個限度, 對象池就會拋出異常.

"出來混, 老是要還的", 池對象也是如此, 當將用完的池對象歸還到對象池中的時候, 對象池會調用池對象工廠對該池對象進行驗證, 若是驗證不經過則被認爲是有問題的對象, 將會被銷燬, 一樣若是容器已經滿了, 這個歸還池對象將變的"無家可歸", 也會被銷燬, 若是不屬於上面兩種狀況, 對象池就會調用工廠對象將其鈍化並放入容器中. 在整個過程當中, 激活, 檢查, 鈍化處理都不是必須的, 所以咱們在實現PoolableObjectFactory接口的時候, 通常不做處理, 給空實現便可, 因此誕生了BasePoolableObjectFactory.

固然你也能夠將要已有的對象建立好, 而後經過addObject放到對象池中去, 以備後用. 

爲了確保對對象池的訪問都是線程安全的, 全部對容器的操做都必須放在synchronized中. 

在apache的common-pool工具庫中有5種對象池:GenericObjectPool和GenericKeyedObjectPool, SoftReferenceObjectPool, StackObjectPool, StackKeyedObjectPool.
五種對象池可分爲兩類, 一類是無key的: 
緩存


另外一類是有key的: 
安全

前面兩種用CursorableLinkedList來作容器, SoftReferenceObjectPool用ArrayList作容器, 一次性建立全部池化對象, 並對容器中的對象進行了軟引用(SoftReference)處理, 從而保證在內存充足的時候池對象不會輕易被jvm垃圾回收, 從而具備很強的緩存能力. 最後兩種用Stack作容器. 不帶key的對象池是對前面池技術原理的一種簡單實現, 帶key的相對複雜一些, 它會將池對象按照key來進行分類, 具備相同的key被劃分到一組類別中, 所以有多少個key, 就會有多少個容器. 之因此須要帶key的這種對象池, 是由於普通的對象池經過makeObject()方法建立的對象基本上都是如出一轍的, 由於無法傳遞參數來對池對象進行定製. 所以四種池對象的區別主要體如今內部的容器的區別, Stack遵循"後進先出"的原則並能保證線程安全, CursorableLinkedList是一個內部用遊標(cursor)來定位當前元素的雙向鏈表, 是非線程安全的, 可是能知足對容器的併發修改.ArrayList是非線程安全的, 便利方便的容器.

使用對象池的通常步驟:建立一個池對象工廠, 將該工廠注入到對象池中, 當要取池對象, 調用borrowObject, 當要歸還池對象時, 調用returnObject, 銷燬池對象調用clear(), 若是要連池對象工廠也一塊兒銷燬, 則調用close().
下面是一些時序圖: 
borrowObject: 
併發


returnObject: 
jvm


invalidateObject: 
函數

apache的鏈接池工具庫common-dbcp是common-pool在數據庫訪問方面的一個具體應用.當對common-pool熟悉以後, 對common-dbcp就很好理解了. 它經過對已有的Connection, Statment對象包裝成池對象PoolableConnection, PoolablePreparedStatement. 而後在這些池化的對象中, 持有一個對對象池的引用, 在關閉的時候, 不進行真正的關閉處理, 而是經過調用:工具

Java代碼   收藏代碼
  1. _pool.returnObject(this);  
  2. 或  
  3.  _pool.returnObject(_key,this);  

 

這樣一句, 將鏈接對象放回鏈接池中. 
而對應的對象池前者採用的是ObjectPool, 後者是KeyedObjectPool, 由於一個數據庫只對應一個鏈接, 而執行操做的Statement卻根據Sql的不一樣會分不少種. 所以須要根據sql語句的不一樣屢次進行緩存
在對鏈接池的管理上, common-dbcp主要採用兩種對象: 
一個是PoolingDriver, 另外一個是PoolingDataSource, 兩者的區別是PoolingDriver是一個更底層的操做類, 它持有一個鏈接池映射列表, 通常針對在一個jvm中要鏈接多個數據庫, 然後者相對簡單一些. 內部只能持有一個鏈接池, 即一個數據源對應一個鏈接池.
下面是common-dbcp的結構關係: 
性能



下面是參考了common-dbcp的例子以後寫的一個從鏈接池中獲取鏈接的工具類

Java代碼   收藏代碼
  1. /** 
  2.  * 建立鏈接 
  3.  *  
  4.  * @since 2009-1-22 下午02:58:35 
  5.  */  
  6. public class ConnectionUtils {  
  7.     // 一些common-dbcp內部定義的protocol  
  8.     private static final String POOL_DRIVER_KEY = "jdbc:apache:commons:dbcp:";  
  9.     private static final String POLLING_DRIVER = "org.apache.commons.dbcp.PoolingDriver";  
  10.   
  11.     /** 
  12.      * 取得池化驅動器 
  13.      *  
  14.      * @return 
  15.      * @throws ClassNotFoundException 
  16.      * @throws SQLException 
  17.      */  
  18.     private static PoolingDriver getPoolDriver() throws ClassNotFoundException,  
  19.             SQLException {  
  20.         Class.forName(POLLING_DRIVER);  
  21.         return (PoolingDriver) DriverManager.getDriver(POOL_DRIVER_KEY);  
  22.     }  
  23.   
  24.     /** 
  25.      * 銷燬全部鏈接 
  26.      *  
  27.      * @throws Exception 
  28.      */  
  29.     public static void destory() throws Exception {  
  30.         PoolingDriver driver = getPoolDriver();  
  31.         String[] names = driver.getPoolNames();  
  32.         for (String name : names) {  
  33.             driver.getConnectionPool(name).close();  
  34.         }  
  35.     }  
  36.   
  37.     /** 
  38.      * 從鏈接池中獲取數據庫鏈接 
  39.      */  
  40.     public static Connection getConnection(TableMetaData table)  
  41.             throws Exception {  
  42.         String key = table.getConnectionKey();  
  43.   
  44.         PoolingDriver driver = getPoolDriver();  
  45.   
  46.         ObjectPool pool = null;  
  47.         // 這裏找不到鏈接池會拋異常, 須要catch一下  
  48.         try {  
  49.             pool = driver.getConnectionPool(key);  
  50.         } catch (Exception e) {  
  51.         }  
  52.           
  53.         if (pool == null) {  
  54.             // 根據數據庫類型構建鏈接工廠  
  55.             ConnectionFactory connectionFactory = null;  
  56.             if (table.getDbAddr() != null  
  57.                     && TableMetaData.DB_TYPE_MYSQL == table.getDbType()) {  
  58.                 Class.forName(TableMetaData.MYSQL_DRIVER);  
  59.                 connectionFactory = new DriverManagerConnectionFactory(table  
  60.                         .getDBUrl(), null);  
  61.             } else {  
  62.                 Class.forName(TableMetaData.ORACLE_DRIVER);  
  63.                 connectionFactory = new DriverManagerConnectionFactory(table  
  64.                         .getDBUrl(), table.getDbuser(), table.getDbpass());  
  65.             }  
  66.               
  67.             // 構造鏈接池  
  68.             ObjectPool connectionPool = new GenericObjectPool(null);  
  69.             new PoolableConnectionFactory(connectionFactory, connectionPool,  
  70.                     nullnullfalsetrue);  
  71.               
  72.             // 將鏈接池註冊到driver中  
  73.             driver.registerPool(key, connectionPool);  
  74.         }  
  75.   
  76.         // 從鏈接池中拿一個鏈接  
  77.         return DriverManager.getConnection(POOL_DRIVER_KEY + key);  
  78.     }  
  79.   
  80. }  

 

雖然對象池技術在實際開發過程當中用的不是不少, 可是理解以後對咱們寫程序仍是有莫大的好處的, 至少我是這樣的

相關文章
相關標籤/搜索