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.javapublic 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。