Change Dir

          先知cd——熱愛生活是一切藝術的開始

          統計

          留言簿(18)

          積分與排名

          “牛”們的博客

          各個公司技術

          我的鏈接

          淘寶技術

          閱讀排行榜

          評論排行榜

          commons-pool學習筆記

          Object pool就是一個管理對象的池子。新版本利用jdk 1.5以后的特性,結合泛型,而不是利用Object來實現了。

          主要就靠3個接口來管理:

          ObjectPool, 定義了池接口,就是說,以后的對象池,至少模子是這個樣子的~~主要兩個實現抽象類:BaseObjectPool和KeyedObjectPool。有一些基本方法比如從對象池取對象,調用borrowObject()方法,將對象放回使用returnObject()方法。清空選擇clear,而不需要對象池選擇關閉時使用close等。在具體的Pool實現中,會依賴PoolableObjectFactory接口。

          PoolableObjectFactory,怎么理解這個接口呢?其實在ObjectPool中我們看到只是聲明了一些調用和放回對象的方法,可以理解為真正我們在操作一個pool,這就是一個數據結構。然而pool中的對象創建、管理、銷毀等事務pool管不了了。因此框架又定義了這樣一個factory去管理這些對象。該接口有makeObject方法,我們自己的factory實現里可以利用這個方法去創建個性化的對象。有創建就有銷毀,destroyObject(T obj)方法就用來銷毀對象。同時還有activateObject(T obj)方法用來激活對象,也就是說令對象可用。有passivateObject(T obj)方法用來將對象作為idle狀態。同時還有validateObject(T obj)方法來檢驗對象是否可以由pool安全返回。

          ObjectPoolFactory,作為最后一個接口,我感覺這個factory應該算是用處不特別大吧。顧名思義,用來管理pool的一個factory。我們的應用如果需要的話,可能不止一個pool。由一個factory去管理,明顯合理的,而且factory模式也是pool框架的特色。該接口只有一個方法,那就是createObjectPool()。

           

          框架的類圖主要關系就是這樣

          clip_image002

          圖片來源是http://commons.apache.org/pool/guide/classdiagrams.html。只需要看清楚幾個接口的關系就可以了。

          那么最好還是舉例看看一個pool如何使用吧。例子是commons-pool1.4版本,jdk1.6

          <代碼修改自參考資料http://www.ibm.com/developerworks/cn/java/l-common-pool/>

          首先定義一個對象(一般使用線程池的原則是對象比較大,所以我們就叫它大對象):

             1: /**
             2:  * 
             3:  */
             4: package common.pool;
             5:  
             6: /**
             7:  * @author Administrator
             8:  *
             9:  */
            10: public class BigObject {
            11:  
            12:     private boolean active;
            13:     
            14:     public boolean isActive() {
            15:         return active;
            16:     }
            17:  
            18:     public void setActive(boolean active) {
            19:         this.active = active;
            20:     }
            21:  
            22:     public BigObject() {
            23:         active = true;
            24:         System.out.println("i am a big object!!!");
            25:     }
            26:     
            27:     public String toString() {
            28:         return "big object "+ this.hashCode();
            29:     }
            30: }

          接著去實現PoolableObjectFactory:

             1: /**
             2:  * 
             3:  */
             4: package common.pool;
             5:  
             6: import org.apache.commons.pool.*;
             7: /**
             8:  * @author Administrator
             9:  *
            10:  */
            11: public class TestFactory implements PoolableObjectFactory{
            12:  
            13:     @Override
            14:     public void activateObject(Object arg0) throws Exception {
            15:         // TODO Auto-generated method stub
            16:         ((BigObject)arg0).setActive(true);
            17:     }
            18:  
            19:     @Override
            20:     public void destroyObject(Object arg0) throws Exception {
            21:         // TODO Auto-generated method stub
            22:         arg0 = null;
            23:     }
            24:  
            25:     @Override
            26:     public Object makeObject() throws Exception {
            27:         // TODO Auto-generated method stub
            28:         BigObject bo = new BigObject();
            29:         return bo;
            30:     }
            31:  
            32:     @Override
            33:     public void passivateObject(Object arg0) throws Exception {
            34:         // TODO Auto-generated method stub
            35:         ((BigObject)arg0).setActive(false);
            36:     }
            37:  
            38:     @Override
            39:     public boolean validateObject(Object arg0) {
            40:         // TODO Auto-generated method stub
            41:         if(((BigObject)arg0).isActive())
            42:             return true;
            43:         else
            44:             return false;
            45:     }
            46:  
            47: }

          最后測試:

             1: /**
             2:  * 
             3:  */
             4: package common.pool;
             5:  
             6: import org.apache.commons.pool.ObjectPool;
             7: import org.apache.commons.pool.PoolableObjectFactory;
             8: import org.apache.commons.pool.impl.StackObjectPool;
             9:  
            10:  
            11: /**
            12:  * @author Administrator
            13:  *
            14:  */
            15: public class PoolTest {
            16:  
            17:     /**
            18:      * @param args
            19:      */
            20:     public static void main(String[] args) {
            21:         // TODO Auto-generated method stub
            22:         BigObject bo = null;
            23:         PoolableObjectFactory factory = new TestFactory();
            24:         ObjectPool pool = new StackObjectPool(factory);
            25:         try {
            26:             for(long i = 0; i < 10 ; i++) {
            27:                 System.out.println("== " + i + " ==");
            28:                 bo = (BigObject) pool.borrowObject();
            29:                 System.out.println(bo);
            30:                 System.out.println(pool.getNumActive());
            31:                 if((i&1)==0) {
            32:                     pool.returnObject(bo);
            33:                 }
            34:             }
            35:            // bo = null;//明確地設為null,作為對象已歸還的標志
            36:         }
            37:         catch (Exception e) {
            38:             e.printStackTrace();
            39:         }
            40:         finally {
            41:             try{
            42:                 if (bo != null) {//避免將一個對象歸還兩次
            43:                     pool.returnObject(bo);
            44:                 }
            45:                 pool.close();
            46:             }
            47:             catch (Exception e){
            48:                 e.printStackTrace();
            49:             }
            50:         }
            51:  
            52:     }
            53:  
            54: }
            55:  

          結束了,一個pool的簡單應用就搞定了。下面我們看看代碼的具體實現是什么。

          先看看pool構造時候都做了些什么:

          StackObjectPool里有兩個protected field,一個protected PoolableObjectFactory _factory = null;一個protected Stack _pool = null;。前者是通過構造方法注入的一個factory,我們的代碼里已經自己實現了一個TestFactory;后者是具體的Pool管理策略,像StackObjectPool的話就是內部用這樣一個Stack來管理對象。

          構造時會通過

             1: public StackObjectPool(PoolableObjectFactory factory, int maxIdle, int initIdleCapacity) {
             2:         _factory = factory;
             3:         _maxSleeping = (maxIdle < 0 ? DEFAULT_MAX_SLEEPING : maxIdle);
             4:         int initcapacity = (initIdleCapacity < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : initIdleCapacity);
             5:         _pool = new Stack();
             6:         _pool.ensureCapacity( initcapacity > _maxSleeping ? _maxSleeping : initcapacity);
             7:     }

          來完成初始參數和變量的構造。

          接著我們開始從pool中取對象了,調用borrowObject的方法,具體實現在源碼中是這樣的:

             1: public synchronized Object borrowObject() throws Exception {
             2:         assertOpen();
             3:         Object obj = null;
             4:         boolean newlyCreated = false;
             5:         while (null == obj) {
             6:             if (!_pool.empty()) {
             7:                 obj = _pool.pop();
             8:             } else {
             9:                 if(null == _factory) {
            10:                     throw new NoSuchElementException();
            11:                 } else {
            12:                     obj = _factory.makeObject();
            13:                     newlyCreated = true;
            14:                   if (obj == null) {
            15:                     throw new NoSuchElementException("PoolableObjectFactory.makeObject() returned null.");
            16:                   }
            17:                 }
            18:             }
            19:             if (null != _factory && null != obj) {
            20:                 try {
            21:                     _factory.activateObject(obj);
            22:                     if (!_factory.validateObject(obj)) {
            23:                         throw new Exception("ValidateObject failed");
            24:                     }
            25:                 } catch (Throwable t) {
            26:                     try {
            27:                         _factory.destroyObject(obj);
            28:                     } catch (Throwable t2) {
            29:                         // swallowed
            30:                     } finally {
            31:                         obj = null;
            32:                     } 
            33:                     if (newlyCreated) {
            34:                         throw new NoSuchElementException(
            35:                             "Could not create a validated object, cause: " +
            36:                             t.getMessage());
            37:                     }
            38:                 }
            39:             }
            40:         }
            41:         _numActive++;
            42:         return obj;
            43:     }

          在不考慮同步的時候,我們只看對象的創建,典型的幾個if邏輯就完成了這樣的功能,在pool空時,就用factory的makeObject方法,不空則直接從內置stack里pop一個對象出來。接著在對象構造OK后,開始激活這個對象。接著調用validate去校驗,如果不合格就跑出異常,而異常的捕獲中又會嘗試去destroy對象,再有問題的話只有swallow掉異常了。整個pool會有一個_numActive變量來控制活著(被borrow但沒被return)的對象數,每次borrow后都會加一。而循環是到obj不空時才退出的,所以obj不會為null。所以,只要你的makeObject合理的不返回null,那么這個邏輯就不會死循環。當然整個過程中似乎都會有多線程安全問題,所以整個方法都加同步。但是這顯然不是完美的解決方法,對于復雜的多線程問題,調度還需要程序員自己來控制。

          接著就是returnObject了,顧名思義就是把從pool中借出來的object還回去。邏輯代碼如下:

             1: public synchronized void returnObject(Object obj) throws Exception {
             2:         boolean success = !isClosed();
             3:         if(null != _factory) {
             4:             if(!_factory.validateObject(obj)) {
             5:                 success = false;
             6:             } else {
             7:                 try {
             8:                     _factory.passivateObject(obj);
             9:                 } catch(Exception e) {
            10:                     success = false;
            11:                 }
            12:             }
            13:         }
            14:  
            15:         boolean shouldDestroy = !success;
            16:  
            17:         _numActive--;
            18:         if (success) {
            19:             Object toBeDestroyed = null;
            20:             if(_pool.size() >= _maxSleeping) {
            21:                 shouldDestroy = true;
            22:                 toBeDestroyed = _pool.remove(0); // remove the stalest object
            23:             }
            24:             _pool.push(obj);
            25:             obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed
            26:         }
            27:         notifyAll(); // _numActive has changed
            28:  
            29:         if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
            30:             try {
            31:                 _factory.destroyObject(obj);
            32:             } catch(Exception e) {
            33:                 // ignored
            34:             }
            35:         }
            36:     }

          首先一個success變量作為判斷位來決定對象能否放回pool。如果經過validate后發現對象不合格,那么顯然不能放回pool了。如果validate合格,那么就嘗試用factory的passivateObject方法使對象變為not active。接著進入邏輯判斷如果可以放回,那么嘗試將對象push到pool的內置stack中,嘗試時會有閾值限定,如果超過了閾值的話,那么需要以某種策略將stack中的對象remove掉一個。而策略如果仔細看代碼就會發現很合理,首先在stack中remove最老的一個,然后將這個remove值保存為臨時變量,將待放回對象push到stack,接著將臨時對象轉接到帶放回對象上。我們看到,這樣的一組行為保證了幾個對象的銷毀,而不會出現內存泄露。

          最后附上apache官網上的幾張時序圖來完美解釋borrow和return的過程:

          clip_image002[4]

          clip_image002[6]

          基本介紹到此,后續可能有進階研究,期待時間~~

          參考資料:

          http://www.ibm.com/developerworks/cn/java/l-common-pool/

          http://commons.apache.org/pool

          posted on 2011-05-06 10:53 changedi 閱讀(6159) 評論(6)  編輯  收藏 所屬分類: Java技術

          評論

          # re: commons-pool學習筆記 2011-05-14 11:19 文學社

          這種結構不錯。  回復  更多評論   

          # re: commons-pool學習筆記[未登錄] 2011-11-09 22:08 刺猬

          我大致瀏覽了下代碼,有兩個問題求解釋:
          1、在判斷idlemax時候,如果到達閾值,remove的是棧底元素,Java stack使用vector實現的,如果remove數組的第一個元素,恐怕在一些vector size比較大情況下性能堪憂,再者,stack里面保留的都是idle狀態的元素,取處于idle最長的意義進行刪除何在? 這又不是LRU
          2、notifyAll 意義? 這個是通知誰 ?   回復  更多評論   

          # re: commons-pool學習筆記[未登錄] 2011-11-10 13:54 changedi

          @刺猬
          1.我想性能問題不算是個問題,這種池結構的設計,只要是這種線性表結構,都難免不了一次remove的數組拷貝,不過一般對象池的維護,大小是自己定的~~肯定不會太大,為了復用,否則new新的對象,交給JVM管理也就是了。
          2.這個問題可以寫個多線程測試的例子,其中同步使用對象鎖,鎖對象就是每個線程應該持有的這個公有的pool。這樣,每次調用pool.wait()就可以讓線程hold等待,在條件滿足時調用一次pool.returnObject(),這樣的notifyAll被調用,一切就都正常了~~
          愚見,歡迎討論  回復  更多評論   

          # re: commons-pool學習筆記[未登錄] 2011-11-10 23:34 刺猬

          謝謝博主答復
          這兩點我是這么看的
          1、其實我從開始就覺得這個類比較雞肋,沒啥大用處。對于這個vector的使用我還是保留意見,認為沒有必要使用,一則清除老idle對象沒有理論依據(或者是至少我沒想到有這種理論依據),二則這里又使用的是vector,如果一定要清楚老對象,完全可以使用linklist之類,在刪除對象可以到O(1)時間復雜度。退者說,沒有實際應用說明我們的對象池對象不會很多,如果我要維持一個size為32的數據庫連接池,對于一些高并發的數據庫請求,會不會出現頻繁的對象新建和釋放。
          2、我贊成博主這個說法,我最初也是這樣猜想,但是如果繼續深挖下去,這個說法同樣困惑:notify的方法不止出現在對象歸還邏輯中,在invalidate對象時同樣會notify操作。Pool調用wait無非是獲取空閑對象發現pool里面沒有空閑對象,釋放Pool鎖進行等待出現空閑對象。那invalidate對象又是通知什么? 再者說,為什么調用Pool的客戶端需要自己控制Pool內部對象的同步互斥——這些本應該直接封裝在borrow方法中的邏輯。從設計上說令人疑惑。
          以前做C的 Java 才入門 有些問題說的不是很清楚 博主見諒   回復  更多評論   

          # re: commons-pool學習筆記[未登錄] 2011-11-11 14:15 changedi

          @刺猬
          深有同感,針對這個同步的問題,其實回答你的時候就發現它惡心了,你說的很對,要么完全交給調用者管理,要么就完全自己管理,這樣半拿半放的讓人困惑~不過代碼簡單,而且又是比較老的東西,能吸收多少就看大家自己了~畢竟是commons下的,可惜了  回復  更多評論   

          # re: commons-pool學習筆記[未登錄] 2011-11-11 23:08 刺猬

          謝謝博主的答復
          回頭看了下genericpool,這個代碼質量好多了  回復  更多評論   

          主站蜘蛛池模板: 开平市| 南安市| 凤台县| 双流县| 会东县| 哈巴河县| 石棉县| 侯马市| 广汉市| 鄂尔多斯市| 东港市| 辉南县| 栖霞市| 南城县| 公安县| 大关县| 宣恩县| 商丘市| 蓬莱市| 茶陵县| 黄石市| 阳原县| 蓬溪县| 新巴尔虎左旗| 永济市| 克拉玛依市| 德阳市| 长白| 崇阳县| 丰台区| 巫溪县| 石楼县| 乳山市| 白沙| 黔西| 乌海市| 扬州市| 金溪县| 乳山市| 肇州县| 永春县|