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包以及文檔
2.編寫測試例子,新建項目只需要導入commons-pool-1.6.jar一個就可以
1)實體對象BaseObject.java
- public class BaseObject {
- //記錄從池中取出次數
- private int num;
- private boolean active;
- public BaseObject(){
- active = true;
- System.out.println("new BaseObject!!!!!");
- }
- //省略get set
2)管理池裏對象的產生,激活,掛起,檢驗和銷燬的工廠類
- public class TestPoolableFactory implements PoolableObjectFactory {
- //重新初始化實例返回池
- @Override
- public void activateObject(Object arg0) throws Exception {
- ((BaseObject)arg0).setActive(true);
- }
- //銷燬被破壞的實例
- @Override
- public void destroyObject(Object arg0) throws Exception {
- arg0 = null;
- }
- //創建一個實例到對象池
- @Override
- public Object makeObject() throws Exception {
- BaseObject bo = new BaseObject();
- return bo;
- }
- //取消初始化實例返回到空閒對象池
- @Override
- public void passivateObject(Object arg0) throws Exception {
- ((BaseObject)arg0).setActive(false);
- }
- //驗證該實例是否安全
- @Override
- public boolean validateObject(Object arg0) {
- if(((BaseObject)arg0).isActive())
- return true;
- else
- return false;
- }
- }
- public class TestKeyPoolableFactory implements KeyedPoolableObjectFactory<String, BaseObject> {
- //重新初始化實例返回池
- @Override
- public void activateObject(String arg0, BaseObject arg1) throws Exception {
- ((BaseObject)arg1).setActive(true);
- }
- //銷燬被破壞的實例
- @Override
- public void destroyObject(String arg0, BaseObject arg1) throws Exception {
- arg1 = null;
- }
- //創建一個實例到對象池
- @Override
- public BaseObject makeObject(String arg0) throws Exception {
- //這裏從數據庫裏查詢出使用次數最少的配置
- BaseObject bo = new BaseObject();
- bo.setNum(0);
- return bo;
- }
- //取消初始化實例返回到空閒對象池
- @Override
- public void passivateObject(String arg0, BaseObject arg1) throws Exception {
- ((BaseObject)arg1).setActive(false);
- }
- //驗證該實例是否安全 true:正在使用
- @Override
- public boolean validateObject(String arg0, BaseObject arg1) {
- //這裏可以判斷實例狀態是否可用
- if(((BaseObject)arg1).isActive())
- return true;
- else
- return false;
- }
- }
3)測試main方法
不帶key的main方法類PoolTest.java- public class PoolTest {
- public static void main(String[] args) {
- BaseObject bo = null;
- PoolableObjectFactory factory = new TestPoolableFactory();
- GenericObjectPool pool = new GenericObjectPool(factory);
- //這裏兩種池都可以,區別下文會提到
- //ObjectPool pool = new StackObjectPool(factory);
- try {
- for(int i = 0; i < 5; i++) {
- System.out.println("\n==========="+i+"===========");
- System.out.println("池中處於閒置狀態的實例pool.getNumIdle():"+pool.getNumIdle());
- //從池裏取一個對象,新創建makeObject或將以前閒置的對象取出來
- bo = (BaseObject)pool.borrowObject();
- System.out.println("bo:"+bo);
- System.out.println("池中所有在用實例數量pool.getNumActive():"+pool.getNumActive());
- if((i%2) == 0) {
- //用完之後歸還對象
- pool.returnObject(bo);
- System.out.println("歸還對象!!!!");
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- if(bo != null) {
- pool.returnObject(bo);
- }
- //關閉池
- pool.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
輸出結果:
- ===========0===========
- 池中處於閒置狀態的實例pool.getNumIdle():0
- new BaseObject!!!!!
- bo:common.keypool.BaseObject@1fdc96c
- 池中所有在用實例數量pool.getNumActive():1
- 歸還對象!!!!
- ===========1===========
- 池中處於閒置狀態的實例pool.getNumIdle():1
- bo:common.keypool.BaseObject@1fdc96c
- 池中所有在用實例數量pool.getNumActive():1
- ===========2===========
- 池中處於閒置狀態的實例pool.getNumIdle():0
- new BaseObject!!!!!
- bo:common.keypool.BaseObject@124bbbf
- 池中所有在用實例數量pool.getNumActive():2
- 歸還對象!!!!
- ===========3===========
- 池中處於閒置狀態的實例pool.getNumIdle():1
- bo:common.keypool.BaseObject@124bbbf
- 池中所有在用實例數量pool.getNumActive():2
- ===========4===========
- 池中處於閒置狀態的實例pool.getNumIdle():0
- new BaseObject!!!!!
- bo:common.keypool.BaseObject@a20892
- 池中所有在用實例數量pool.getNumActive():3
- 歸還對象!!!!
這裏的池聲明用ObjectPool或者GenericObjectPool的區別在於:
ObjectPool這種對象池的特點是:
- 可以爲對象池指定一個初始的參考大小(當空間不夠時會自動增長)。
- 在對象池已空的時候,調用它的borrowObject方法,會自動返回新創建的實例。
- 可以爲對象池指定一個可保存的對象數目的上限。達到這個上限之後,再向池裏送回的對象會被自動送去回收。
GenericObjectPool這種對象池的特色是:
- 可以設定最多能從池中借出多少個對象。
- 可以設定池中最多能保存多少個對象。
- 可以設定在池中已無對象可借的情況下,調用它的borrowObject方法時的行爲,是等待、創建新的實例還是拋出異常。
- 可以分別設定對象借出和還回時,是否進行有效性檢查。
- 可以設定是否使用一個單獨的線程,對池內對象進行後臺清理。
- ……
源碼GenericKeyedObjectPool.java類內部的setConfig方法
- /**
- * Sets the configuration.
- * @param conf the new configuration to use.
- * @see GenericKeyedObjectPool.Config
- */
- public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {
- setMaxIdle(conf.maxIdle);
- setMaxActive(conf.maxActive);
- setMaxTotal(conf.maxTotal);
- setMinIdle(conf.minIdle);
- setMaxWait(conf.maxWait);
- setWhenExhaustedAction(conf.whenExhaustedAction);
- setTestOnBorrow(conf.testOnBorrow);
- setTestOnReturn(conf.testOnReturn);
- setTestWhileIdle(conf.testWhileIdle);
- setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
- setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
- setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
- }
源碼GenericObjectPool.java類內部的setConfig方法
- /**
- * Sets my configuration.
- *
- * @param conf configuration to use.
- * @see GenericObjectPool.Config
- */
- public void setConfig(GenericObjectPool.Config conf) {
- synchronized (this) {
- setMaxIdle(conf.maxIdle);
- setMinIdle(conf.minIdle);
- setMaxActive(conf.maxActive);
- setMaxWait(conf.maxWait);
- setWhenExhaustedAction(conf.whenExhaustedAction);
- setTestOnBorrow(conf.testOnBorrow);
- setTestOnReturn(conf.testOnReturn);
- setTestWhileIdle(conf.testWhileIdle);
- setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
- setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
- setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
- setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis);
- setLifo(conf.lifo);
- }
- allocate();
- }
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
- public class KeyPoolTest {
- public static void main(String[] args) {
- BaseObject bo = null;
- BaseObject bo1 = null;
- BaseObject bo2 = null;
- KeyedPoolableObjectFactory<String, BaseObject> keyFactory = new TestKeyPoolableFactory();
- GenericKeyedObjectPool<String, BaseObject> keyPool = new GenericKeyedObjectPool<String, BaseObject>(keyFactory);
- //keyPool.setLifo(false);
- try {
- //這裏添加池對象,只需要傳入key就會默認調用makeObject()方法創建一個對象
- keyPool.addObject("一級");
- keyPool.addObject("二級");
- //這裏註釋掉,不初始創建這個鍵的池對象
- //keyPool.addObject("三級");
- System.out.println("池中處於閒置狀態的實例pool.getNumIdle():"+keyPool.getNumIdle());
- for (int i = 0; i < 5; i++) {
- //從池裏取對象
- bo = keyPool.borrowObject("一級");
- bo.setNum(bo.getNum()+1);
- System.out.println("一級"+i+"-------"+bo+"-------"+bo.getNum());
- bo1 = keyPool.borrowObject("二級");
- bo1.setNum(bo1.getNum()+1);
- System.out.println("二級"+i+"-------"+bo1+"-------"+bo1.getNum());
- //上邊註釋掉的那行代碼,這裏取對象的時候如果沒有閒置對象,也會默認去創建一個key="三級"的池對象
- bo2 = keyPool.borrowObject("三級");
- bo2.setNum(bo2.getNum()+1);
- System.out.println("三級"+i+"-------"+bo2+"-------"+bo2.getNum());
- if(i<3) {
- //用完之後歸還對象
- keyPool.returnObject("一級", bo);
- keyPool.returnObject("二級", bo1);
- keyPool.returnObject("三級", bo2);
- System.out.println("歸還對象!!!");
- }
- }
- //當前池裏的實例數量
- System.out.println("池中所有在用實例pool.getNumActive():"+keyPool.getNumActive());
- //當前池裏的處於閒置狀態的實例
- System.out.println("池中處於閒置狀態的實例pool.getNumIdle():"+keyPool.getNumIdle());
- } catch (Exception e) {
- e.printStackTrace();
- }
- //這裏就不寫finally了,偷懶了,這裏應該關閉池的
- }
- }
輸出結果:
- new BaseObject!!!!!
- new BaseObject!!!!!
- 池中處於閒置狀態的實例pool.getNumIdle():2
- 一級0-------common.keypool.BaseObject@158b649-------1
- 二級0-------common.keypool.BaseObject@127734f-------1
- new BaseObject!!!!!
- 三級0-------common.keypool.BaseObject@1037c71-------1
- 歸還對象!!!
- 一級1-------common.keypool.BaseObject@158b649-------2
- 二級1-------common.keypool.BaseObject@127734f-------2
- 三級1-------common.keypool.BaseObject@1037c71-------2
- 歸還對象!!!
- 一級2-------common.keypool.BaseObject@158b649-------3
- 二級2-------common.keypool.BaseObject@127734f-------3
- 三級2-------common.keypool.BaseObject@1037c71-------3
- 歸還對象!!!
- 一級3-------common.keypool.BaseObject@158b649-------4
- 二級3-------common.keypool.BaseObject@127734f-------4
- 三級3-------common.keypool.BaseObject@1037c71-------4
- new BaseObject!!!!!
- 一級4-------common.keypool.BaseObject@1df073d-------1
- new BaseObject!!!!!
- 二級4-------common.keypool.BaseObject@1546e25-------1
- new BaseObject!!!!!
- 三級4-------common.keypool.BaseObject@b66cc-------1
- 池中所有在用實例pool.getNumActive():6
- 池中處於閒置狀態的實例pool.getNumIdle():0
通過輸出結果可以看出:
1.對象取出之後可以對對象進行更改,再放回池裏這個更改是保留的。
2.池對象的放入和取出默認是後進先出的原則,可以通過池pool的setLifo(boolean lifo)方法設置,默認是true,代表後進先出,設置爲false代表先進先出。如果爲了池對象使用均衡,推薦使用false。
原文鏈接:http://blog.csdn.net/bhy5683/article/details/8776498