Apache Commons-pool實現對象池(包括帶key對象池)

Commons-pool是一個apache開源組織下的衆多項目的一個。其被廣泛地整合到衆多需要對象池功能的項目中。

官網:http://commons.apache.org/proper/commons-pool/

本文是commons-pool的一個簡單應用,包括不帶key的池和帶key的池。帶key的池是把key相同的池對象放在一起池裏,也就是說有多少個key就有多少個池。

不帶key的池是生產完全一致的對象放在池裏,但是有時候,單用對池內所有對象一視同仁的對象池,並不能解決的問題。例如,對於一組某些參數設置不同的同類對象――比如一堆指向不同地址的 java.net.URL對象或者一批代表不同語句的java.sql.PreparedStatement對象,用這樣的方法池化,就有可能取出不合用 的對象的麻煩。這裏對帶key的池也做了簡單的例子。

Commons-pool將對象池化的工作安排給了三類對象:

1.      PoolableObjectFactory(KeyedPoolableObjectFactory):用於管理被池化對象的產生,激活,掛起,檢驗和銷燬。

2.      ObjectPool(KeyedObjectPool):用於管理要被池化的對象的接觸和歸還,並通過PoolableObjectFactory完成相應的操作。

3.      ObjectPoolFactory(KeyedObjectPoolFactory):ObjectPool的工廠,裏邊有createPool()方法,用於大量生成相同類型和設置的池。

1.下載相關jar包以及文檔

下載API以及相關jar包,本文用到的是目前最新的1.6版本。
下載源碼以及官方例子,可供參考。

2.編寫測試例子,新建項目只需要導入commons-pool-1.6.jar一個就可以

1)實體對象BaseObject.java

[java] view plaincopy
  1. public class BaseObject {  
  2.   
  3.     //記錄從池中取出次數  
  4.     private int num;  
  5.     private boolean active;  
  6.       
  7.     public BaseObject(){  
  8.         active = true;  
  9.         System.out.println("new BaseObject!!!!!");  
  10.     }  
  11. //省略get set  

2)管理池裏對象的產生,激活,掛起,檢驗和銷燬的工廠類

工廠類裏的方法內部可以自己根據業務邏輯去寫,例如:
makeObject()方法內部可以查詢數據庫封裝成對象,注意一次只能創建一個池對象。
validateObject()方法內部可以自己驗證該對象是否正在使用,比如可以通過對象的狀態驗證。
這是不帶key的工廠類TestPoolableFactory.java
[java] view plaincopy
  1. public class TestPoolableFactory implements PoolableObjectFactory {  
  2.   
  3.     //重新初始化實例返回池  
  4.     @Override  
  5.     public void activateObject(Object arg0) throws Exception {  
  6.         ((BaseObject)arg0).setActive(true);  
  7.     }  
  8.   
  9.     //銷燬被破壞的實例  
  10.     @Override  
  11.     public void destroyObject(Object arg0) throws Exception {  
  12.         arg0 = null;  
  13.     }  
  14.   
  15.     //創建一個實例到對象池  
  16.     @Override  
  17.     public Object makeObject() throws Exception {  
  18.         BaseObject bo = new BaseObject();  
  19.         return bo;  
  20.     }  
  21.   
  22.     //取消初始化實例返回到空閒對象池  
  23.     @Override  
  24.     public void passivateObject(Object arg0) throws Exception {  
  25.         ((BaseObject)arg0).setActive(false);  
  26.     }  
  27.   
  28.     //驗證該實例是否安全  
  29.     @Override  
  30.     public boolean validateObject(Object arg0) {  
  31.         if(((BaseObject)arg0).isActive())  
  32.             return true;  
  33.         else  
  34.             return false;  
  35.     }  
  36.   
  37. }  


這是帶key的工廠類TestKeyPoolableFactory.java
[java] view plaincopy
  1. public class TestKeyPoolableFactory implements KeyedPoolableObjectFactory<String, BaseObject> {  
  2.   
  3.     //重新初始化實例返回池  
  4.     @Override  
  5.     public void activateObject(String arg0, BaseObject arg1) throws Exception {  
  6.         ((BaseObject)arg1).setActive(true);  
  7.     }  
  8.   
  9.     //銷燬被破壞的實例  
  10.     @Override  
  11.     public void destroyObject(String arg0, BaseObject arg1) throws Exception {  
  12.         arg1 = null;  
  13.     }  
  14.   
  15.     //創建一個實例到對象池  
  16.     @Override  
  17.     public BaseObject makeObject(String arg0) throws Exception {  
  18.         //這裏從數據庫裏查詢出使用次數最少的配置  
  19.         BaseObject bo = new BaseObject();  
  20.         bo.setNum(0);  
  21.         return bo;  
  22.     }  
  23.   
  24.     //取消初始化實例返回到空閒對象池  
  25.     @Override  
  26.     public void passivateObject(String arg0, BaseObject arg1) throws Exception {  
  27.         ((BaseObject)arg1).setActive(false);  
  28.     }  
  29.   
  30.     //驗證該實例是否安全 true:正在使用  
  31.     @Override  
  32.     public boolean validateObject(String arg0, BaseObject arg1) {  
  33.         //這裏可以判斷實例狀態是否可用  
  34.         if(((BaseObject)arg1).isActive())  
  35.             return true;  
  36.         else  
  37.             return false;  
  38.     }  
  39. }  

3)測試main方法

不帶key的main方法類PoolTest.java
[java] view plaincopy
  1. public class PoolTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         BaseObject bo = null;  
  5.         PoolableObjectFactory factory = new TestPoolableFactory();  
  6.         GenericObjectPool pool = new GenericObjectPool(factory);  
  7.         //這裏兩種池都可以,區別下文會提到  
  8.         //ObjectPool pool = new StackObjectPool(factory);  
  9.         try {  
  10.             for(int i = 0; i < 5; i++) {  
  11.                 System.out.println("\n==========="+i+"===========");  
  12.                 System.out.println("池中處於閒置狀態的實例pool.getNumIdle():"+pool.getNumIdle());  
  13.                 //從池裏取一個對象,新創建makeObject或將以前閒置的對象取出來  
  14.                 bo = (BaseObject)pool.borrowObject();  
  15.                 System.out.println("bo:"+bo);  
  16.                 System.out.println("池中所有在用實例數量pool.getNumActive():"+pool.getNumActive());  
  17.                 if((i%2) == 0) {  
  18.                     //用完之後歸還對象  
  19.                     pool.returnObject(bo);  
  20.                     System.out.println("歸還對象!!!!");  
  21.                 }  
  22.             }  
  23.         } catch (Exception e) {  
  24.             e.printStackTrace();  
  25.         } finally {  
  26.             try {  
  27.                 if(bo != null) {  
  28.                     pool.returnObject(bo);  
  29.                 }  
  30.                 //關閉池  
  31.                 pool.close();  
  32.             } catch (Exception e) {  
  33.                 e.printStackTrace();  
  34.             }  
  35.         }  
  36.     }  
  37. }  


輸出結果:

[plain] view plaincopy
  1. ===========0===========  
  2. 池中處於閒置狀態的實例pool.getNumIdle():0  
  3. new BaseObject!!!!!  
  4. bo:common.keypool.BaseObject@1fdc96c  
  5. 池中所有在用實例數量pool.getNumActive():1  
  6. 歸還對象!!!!  
  7.   
  8. ===========1===========  
  9. 池中處於閒置狀態的實例pool.getNumIdle():1  
  10. bo:common.keypool.BaseObject@1fdc96c  
  11. 池中所有在用實例數量pool.getNumActive():1  
  12.   
  13. ===========2===========  
  14. 池中處於閒置狀態的實例pool.getNumIdle():0  
  15. new BaseObject!!!!!  
  16. bo:common.keypool.BaseObject@124bbbf  
  17. 池中所有在用實例數量pool.getNumActive():2  
  18. 歸還對象!!!!  
  19.   
  20. ===========3===========  
  21. 池中處於閒置狀態的實例pool.getNumIdle():1  
  22. bo:common.keypool.BaseObject@124bbbf  
  23. 池中所有在用實例數量pool.getNumActive():2  
  24.   
  25. ===========4===========  
  26. 池中處於閒置狀態的實例pool.getNumIdle():0  
  27. new BaseObject!!!!!  
  28. bo:common.keypool.BaseObject@a20892  
  29. 池中所有在用實例數量pool.getNumActive():3  
  30. 歸還對象!!!!  

這裏的池聲明用ObjectPool或者GenericObjectPool的區別在於:

ObjectPool這種對象池的特點是:

  • 可以爲對象池指定一個初始的參考大小(當空間不夠時會自動增長)。
  • 在對象池已空的時候,調用它的borrowObject方法,會自動返回新創建的實例。
  • 可以爲對象池指定一個可保存的對象數目的上限。達到這個上限之後,再向池裏送回的對象會被自動送去回收。

GenericObjectPool這種對象池的特色是:

  • 可以設定最多能從池中借出多少個對象。
  • 可以設定池中最多能保存多少個對象。
  • 可以設定在池中已無對象可借的情況下,調用它的borrowObject方法時的行爲,是等待、創建新的實例還是拋出異常。
  • 可以分別設定對象借出和還回時,是否進行有效性檢查。
  • 可以設定是否使用一個單獨的線程,對池內對象進行後臺清理。
  • ……
這些參數可以通過一個Config類來管理,然後通過GenericObjectPool的setConfig(Config conf)方法來設置自定義的Config類。下面附上setConfig方法源碼,就知道Config類需要哪些參數了:

源碼GenericKeyedObjectPool.java類內部的setConfig方法

[java] view plaincopy
  1. /** 
  2.      * Sets the configuration. 
  3.      * @param conf the new configuration to use. 
  4.      * @see GenericKeyedObjectPool.Config 
  5.      */  
  6.     public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {  
  7.         setMaxIdle(conf.maxIdle);  
  8.         setMaxActive(conf.maxActive);  
  9.         setMaxTotal(conf.maxTotal);  
  10.         setMinIdle(conf.minIdle);  
  11.         setMaxWait(conf.maxWait);  
  12.         setWhenExhaustedAction(conf.whenExhaustedAction);  
  13.         setTestOnBorrow(conf.testOnBorrow);  
  14.         setTestOnReturn(conf.testOnReturn);  
  15.         setTestWhileIdle(conf.testWhileIdle);  
  16.         setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);  
  17.         setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);  
  18.         setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);  
  19.     }  

源碼GenericObjectPool.java類內部的setConfig方法

[java] view plaincopy
  1. /** 
  2.      * Sets my configuration. 
  3.      * 
  4.      * @param conf configuration to use. 
  5.      * @see GenericObjectPool.Config 
  6.      */  
  7.     public void setConfig(GenericObjectPool.Config conf) {  
  8.         synchronized (this) {  
  9.             setMaxIdle(conf.maxIdle);  
  10.             setMinIdle(conf.minIdle);  
  11.             setMaxActive(conf.maxActive);  
  12.             setMaxWait(conf.maxWait);  
  13.             setWhenExhaustedAction(conf.whenExhaustedAction);  
  14.             setTestOnBorrow(conf.testOnBorrow);  
  15.             setTestOnReturn(conf.testOnReturn);  
  16.             setTestWhileIdle(conf.testWhileIdle);  
  17.             setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);  
  18.             setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);  
  19.             setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);  
  20.             setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis);  
  21.             setLifo(conf.lifo);  
  22.         }  
  23.         allocate();  
  24.     }  

1.      參數maxActive指明能從池中借出的對象的最大數目。如果這個值不是正數,表示沒有限制。

2.      參數whenExhaustedA ction指定在池中借出對象的數目已達極限的情況下,調用它的borrowObject方法時的行爲。可以選用的值有:

  • GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
  • GenericObjectPool.WHEN_EXHAUSTED_GROW,表示創建新的實例(不過這就使maxActive參數失去了意義);
  • GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示拋出一個java.util.NoSuchElementException異常。

3.      參數maxWait指明若在對象池空時調用borrowObject方法的行爲被設定成等待,最多等待多少毫秒。如果等待時間超過了這個數值,則會拋出一個java.util.NoSuchElementException異常。如果這個值不是正數,表示無限期等待。

4.      參數testOnBorrow設定在借出對象時是否進行有效性檢查。

5.      參數testOnBorrow設定在還回對象時是否進行有效性檢查。

6.      參數timeBetweenEvictionRunsMillis,設定間隔每過多少毫秒進行一次後臺對象清理的行動。如果這個值不是正數,則實際上不會進行後臺對象清理。

7.      參數minEvictableIdleTimeMillis,設定在進行後臺對象清理時,視休眠時間超過了多少毫秒的對象爲過期。過期的對象將被回收。如果這個值不是正數,那麼對休眠時間沒有特別的約束。

8.      參數testWhileIdle,則設定在進行後臺對象清理時,是否還對沒有過期的池內對象進行有效性檢查。不能通過有效性檢查的對象也將被回收。

9.      參數lifo,池對象的放入和取出默認是後進先出的原則,默認是true,代表後進後出,設置爲false代表先進先出。


帶key的main方法類KeyPoolTest.java

[java] view plaincopy
  1. public class KeyPoolTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         BaseObject bo = null;  
  6.         BaseObject bo1 = null;  
  7.         BaseObject bo2 = null;  
  8.           
  9.         KeyedPoolableObjectFactory<String, BaseObject> keyFactory = new TestKeyPoolableFactory();  
  10.         GenericKeyedObjectPool<String, BaseObject> keyPool = new GenericKeyedObjectPool<String, BaseObject>(keyFactory);  
  11.         //keyPool.setLifo(false);  
  12.         try {  
  13.             //這裏添加池對象,只需要傳入key就會默認調用makeObject()方法創建一個對象  
  14.             keyPool.addObject("一級");  
  15.             keyPool.addObject("二級");  
  16.             //這裏註釋掉,不初始創建這個鍵的池對象  
  17.             //keyPool.addObject("三級");  
  18.             System.out.println("池中處於閒置狀態的實例pool.getNumIdle():"+keyPool.getNumIdle());  
  19.             for (int i = 0; i < 5; i++) {  
  20.                 //從池裏取對象  
  21.                 bo = keyPool.borrowObject("一級");  
  22.                 bo.setNum(bo.getNum()+1);  
  23.                 System.out.println("一級"+i+"-------"+bo+"-------"+bo.getNum());  
  24.                   
  25.                 bo1 = keyPool.borrowObject("二級");  
  26.                 bo1.setNum(bo1.getNum()+1);  
  27.                 System.out.println("二級"+i+"-------"+bo1+"-------"+bo1.getNum());  
  28.                 //上邊註釋掉的那行代碼,這裏取對象的時候如果沒有閒置對象,也會默認去創建一個key="三級"的池對象  
  29.                 bo2 = keyPool.borrowObject("三級");  
  30.                 bo2.setNum(bo2.getNum()+1);  
  31.                 System.out.println("三級"+i+"-------"+bo2+"-------"+bo2.getNum());  
  32.                   
  33.                 if(i<3) {  
  34.                     //用完之後歸還對象  
  35.                     keyPool.returnObject("一級", bo);  
  36.                     keyPool.returnObject("二級", bo1);  
  37.                     keyPool.returnObject("三級", bo2);  
  38.                     System.out.println("歸還對象!!!");  
  39.                 }  
  40.             }  
  41.             //當前池裏的實例數量  
  42.             System.out.println("池中所有在用實例pool.getNumActive():"+keyPool.getNumActive());  
  43.             //當前池裏的處於閒置狀態的實例  
  44.             System.out.println("池中處於閒置狀態的實例pool.getNumIdle():"+keyPool.getNumIdle());  
  45.         } catch (Exception e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.         //這裏就不寫finally了,偷懶了,這裏應該關閉池的  
  49.     }  
  50. }  

輸出結果:

[plain] view plaincopy
  1. new BaseObject!!!!!  
  2. new BaseObject!!!!!  
  3. 池中處於閒置狀態的實例pool.getNumIdle():2  
  4. 一級0-------common.keypool.BaseObject@158b649-------1  
  5. 二級0-------common.keypool.BaseObject@127734f-------1  
  6. new BaseObject!!!!!  
  7. 三級0-------common.keypool.BaseObject@1037c71-------1  
  8. 歸還對象!!!  
  9. 一級1-------common.keypool.BaseObject@158b649-------2  
  10. 二級1-------common.keypool.BaseObject@127734f-------2  
  11. 三級1-------common.keypool.BaseObject@1037c71-------2  
  12. 歸還對象!!!  
  13. 一級2-------common.keypool.BaseObject@158b649-------3  
  14. 二級2-------common.keypool.BaseObject@127734f-------3  
  15. 三級2-------common.keypool.BaseObject@1037c71-------3  
  16. 歸還對象!!!  
  17. 一級3-------common.keypool.BaseObject@158b649-------4  
  18. 二級3-------common.keypool.BaseObject@127734f-------4  
  19. 三級3-------common.keypool.BaseObject@1037c71-------4  
  20. new BaseObject!!!!!  
  21. 一級4-------common.keypool.BaseObject@1df073d-------1  
  22. new BaseObject!!!!!  
  23. 二級4-------common.keypool.BaseObject@1546e25-------1  
  24. new BaseObject!!!!!  
  25. 三級4-------common.keypool.BaseObject@b66cc-------1  
  26. 池中所有在用實例pool.getNumActive():6  
  27. 池中處於閒置狀態的實例pool.getNumIdle():0  

通過輸出結果可以看出:

1.對象取出之後可以對對象進行更改,再放回池裏這個更改是保留的。

2.池對象的放入和取出默認是後進先出的原則,可以通過池pool的setLifo(boolean lifo)方法設置,默認是true,代表後進先出,設置爲false代表先進先出。如果爲了池對象使用均衡,推薦使用false。


原文鏈接:http://blog.csdn.net/bhy5683/article/details/8776498

發佈了5 篇原創文章 · 獲贊 25 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章