-
是什么
apache commons-pool是apache基金會的一個開源對象池組件,我們常用的數據庫連接池dpcp和redis的java客戶端jedis都使用commons-pool來管理連接
優化對象的創建,和設計模式中的享元模式思路一樣
-
類解析
PooledObject 池化后的對象
ObjectPool 對象池,
PooledObjectFactory 池對象工廠
-
GenericObjectPool
實現了對對象池的管理,是一個基本的對象池實現
borrowObject
從對象池中獲取一個對象
returnObject
對象使用完之后,歸還到對象池
-
PooledObjectFactory
根據自己的業務創建和管理要對象池化的對象
makeObject
創建對象
destroyObject
銷毀對象
- 對象的空閑時間(idle)超時
- 使用完被檢測到對象已經無效時
當調用這個方法之后,對象的生命周期必須結束。如果是對象是線程,線程必須已結束,如果是socket,socket必須已close,如果是文件操作,文件數據必須已flush,且文件正常關閉.
validateObject
檢測一個對象是否有效,無效會被銷毀
activateObject
激活一個對象或者說啟動對象的某些操作
- 檢測空閑對象的時候,且設置了測試空閑對象是否可以用,就會調用這個方法.
-
borrowObject
的時候 - 如果對象是一個包含參數的對象,可以在這里進行初始化
passivateObject
鈍化對象
在向對象池歸還一個對象是會調用這個方法。這里可以對對象做一些清理操作。比如清理掉過期的數據,下次獲得對象時,不受舊數據的影響
一般來說activateObject和passivateObject是成對出現的。前者是在對象從對象池取出時做一些操作,后者是在對象歸還到對象池做一些操作,可以根據自己的業務需要進行取舍。
-
參數配置類GenericObjectPoolConfig
lifo: 對象池存儲空閑對象是使用的LinkedBlockingDeque,建議使用默認值true
fairness: 是否使用lock的公平鎖(不公平的性能高5-10倍,獲取鎖時沒排隊,沒有先到先得的概念)。默認值是false,建議使用默認值。
maxWaitMillis: 當沒有空閑連接時,獲取一個對象的最大等待時間。如果這個值小于0,則永不超時,一直等待,直到有空閑對象到來。如果大于0,則等待maxWaitMillis長時間,如果沒有空閑對象,將拋出NoSuchElementException異常。默認值是-1;可以根據需要自己調整,單位是毫秒。
minEvictableIdleTimeMillis: 當空閑的時間大于這個值時,執行移除這個對象操作,默認值30分鐘。這個參數是強制性的,只要空閑時間超過這個值,就會移除.小于0則為Long的最大值
softMinEvictableIdleTimeMillis: 對象最小的空閑時間 ,和minEvictableIdleTimeMillis
邏輯一樣,區別是:它會保留最小的空閑對象數量。而上面的不會,是強制性移除的。默認值是-1;
numTestsPerEvictionRun: 檢測空閑對象線程每次檢測的空閑對象的數量。默認值是3;如果這個值小于0,則每次檢測的空閑對象數量等于當前空閑對象數量除以這個值的絕對值,并對結果向上取整
testOnCreate: 在創建對象時檢測對象是否有效,true是,默認值是false。
testOnBorrow: 在從對象池獲取對象時是否檢測對象有效,true是;默認值是false。
testOnReturn: 在向對象池中歸還對象時是否檢測對象有效,true是,默認值是false。
testWhileIdle: 在檢測空閑對象線程檢測到對象不需要移除時,是否檢測對象的有效性。true是,默認值是false。
timeBetweenEvictionRunsMillis:空閑對象檢測線程的執行周期,即多長時候執行一次空閑對象檢測。單位是毫秒數。如果小于等于0,則不執行檢測線程。默認值是-1;
blockWhenExhausted: 當對象池沒有空閑對象時,新的獲取對象的請求是否阻塞。true阻塞。默認值是true;
maxTotal:對象池中管理的最多對象個數。默認值是8。
maxIdle:對象池中最大的空閑對象個數。默認值是8。
minIdle:對象池中最小的空閑對象個數。默認值是0。
-
怎么用
- 添加依賴
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.3</version>
</dependency>
- 需要池化的對象,緩存的對象
public class Resource {
private static int id;
private int rid;
public Resource() {
synchronized (this) {
this.rid = id++;
}
}
public int getRid() {
return this.rid;
}
@Override
public String toString() {
return "id:" + this.rid;
}
}
- 創建工廠類
public class MyPoolableObjectFactory extends BasePooledObjectFactory<Resource>{
/**
* 創建一個對象實例
*/
@Override
public Resource create() throws Exception {
return new Resource();
}
/**
* 包裹創建的對象實例,返回一個pooledobject
*/
@Override
public PooledObject<Resource> wrap(Resource obj) {
return new DefaultPooledObject<Resource>(obj);
}
}
- 客戶端調用
public class Test {
public static void main(String[] args) {
// 創建池對象工廠
PooledObjectFactory<Resource> factory = new MyPoolableObjectFactory();
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// 最大空閑數
poolConfig.setMaxIdle(5);
// 最小空閑數, 池中只有一個空閑對象的時候,池會在創建一個對象,并借出一個對象,從而保證池中最小空閑數為1
poolConfig.setMinIdle(1);
// 最大池對象總數
poolConfig.setMaxTotal(20);
// 逐出連接的最小空閑時間 默認1800000毫秒(30分鐘)
poolConfig.setMinEvictableIdleTimeMillis(1800000);
// 逐出掃描的時間間隔(毫秒) 如果為負數,則不運行逐出線程, 默認-1
poolConfig.setTimeBetweenEvictionRunsMillis(1800000 * 2L);
// 在獲取對象的時候檢查有效性, 默認false
poolConfig.setTestOnBorrow(true);
// 在歸還對象的時候檢查有效性, 默認false
poolConfig.setTestOnReturn(false);
// 在空閑時檢查有效性, 默認false
poolConfig.setTestWhileIdle(false);
// 最大等待時間, 默認的值為-1,表示無限等待。
poolConfig.setMaxWaitMillis(5000);
// 是否啟用后進先出, 默認true
poolConfig.setLifo(true);
// 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true
poolConfig.setBlockWhenExhausted(true);
// 每次逐出檢查時 逐出的最大數目 默認3
poolConfig.setNumTestsPerEvictionRun(3);
// 創建對象池
final GenericObjectPool<Resource> pool = new GenericObjectPool<Resource>(factory, poolConfig);
for (int i = 0; i < 40; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Resource resource = pool.borrowObject();// 注意,如果對象池沒有空余的對象,那么這里會block,可以設置block的超時時間
System.out.println(resource);
Thread.sleep(1000);
pool.returnObject(resource);// 申請的資源用完了記得歸還,不然其他人要申請時可能就沒有資源用了
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
}
-
例二:
- 創建對象
/**
* FileName :Conn
* Author :zengzhijun
* Date : 2018/5/30 19:41
* Description:
*/
package com.byedbl.pool.example1;
import org.slf4j.LoggerFactory;
/**
* common-pool2 使用方式
* <p/>
* 假設這是一個建立TCP連接的對象,該對象的初始化時間平均為500ms,為了避免在程序中頻繁創建Conn對象,我們需要借助對象池管理Conn對象實例
*
* @author : zengzhijun
* @date : 2018/5/30 19:42
**/
public class Conn {
/**
* 記錄對象的創建時間
*/
private long createTime;
/**
* 初始化Conn對象,模擬創建Conn對象平均消耗500ms
* @throws InterruptedException
*/
public Conn() throws InterruptedException {
Thread.sleep(500);
createTime = System.currentTimeMillis();
LoggerFactory.getLogger(getClass()).debug(" init conn suc... " + createTime);
}
/**
* 報告Conn對象信息
*/
public void report() {
LoggerFactory.getLogger(getClass()).info("this is a available conn " + createTime);
}
}
- 創建對象的工廠
/**
* FileName :ConnFactory
* Author :zengzhijun
* Date : 2018/5/30 19:46
* Description:
*/
package com.byedbl.pool.example1;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
* common-pool2 使用方式
* <p/>
* 為了使用common-pool2對象池管理,我們必須實現{@link org.apache.commons.pool2.PooledObjectFactory}或者其子類
* 這是一個工廠模式,告訴對象池怎樣去創建要管理的對象
* <p/>
* BasePooledObjectFactory 是對{@link org.apache.commons.pool2.PooledObjectFactory}的一個基本實現,我們可以繼承該類,減少一些方法的實現
* <p/>
* 在實現{@link org.apache.commons.pool2.PooledObjectFactory}接口時,我們一定要實現的接口方法是{@link org.apache.commons.pool2.PooledObjectFactory#makeObject()}方法。
*
* @author : zengzhijun
* @date : 2018/5/30 19:46
**/
public class ConnFactory extends BasePooledObjectFactory<Conn> {
/**
* 間接實現{@link org.apache.commons.pool2.PooledObjectFactory#makeObject()}方法,表明怎樣創建需要管理對象
*/
@Override
public Conn create() throws Exception {
return new Conn();
}
/**
* 在common-pool2中為了統計管理的對象的一些信息,比如調用次數,空閑時間,上次使用時間等,需要對管理的對象進行包裝,然后在放入到對象池中
*
* @param obj 對象池要管理的對象
* @return 返回包裝后的PooledObject對象
*/
@Override
public PooledObject<Conn> wrap(Conn obj) {
return new DefaultPooledObject<Conn>(obj);
}
}
- 創建對象池
/**
* FileName :ConnPool
* Author :zengzhijun
* Date : 2018/5/30 19:51
* Description:
*/
package com.byedbl.pool.example1;
import org.apache.commons.pool2.impl.GenericObjectPool;
/**
* common-pool2 使用方式
* <p/>
* Conn對象管理池,這里利用 GenericObjectPool 作為對象池
*
* @author : zengzhijun
* @date : 2018/5/30 19:52
**/
public class ConnPool extends GenericObjectPool<Conn> {
/**
* 調用{@link GenericObjectPool}的構造方法,構造ConnPool
*/
public ConnPool() {
super(new ConnFactory(), new ConnPoolConfig());
}
/**
* 調用{@link GenericObjectPool}的構造方法,構造ConnPool
*/
public ConnPool(ConnPoolConfig connPoolConfig) {
super(new ConnFactory(), connPoolConfig);
}
}
- 對象池配置
/**
* FileName :ConnPoolConfig
* Author :zengzhijun
* Date : 2018/5/30 19:50
* Description:
*/
package com.byedbl.pool.example1;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
/**
* common-pool2 使用方式
* <p/>
* {@link org.apache.commons.pool2.impl.GenericObjectPool}支持個性化配置,我們可以配置對象池中總共的對象數,最大、最小空閑對象數等等
* 這邊繼承{@link GenericObjectPoolConfig}是為了ConnPool也可以進行個性化的配置
*
* @author : zengzhijun
* @date : 2018/5/30 19:50
**/
public class ConnPoolConfig extends GenericObjectPoolConfig {
public ConnPoolConfig() {
// defaults to make your life with connection pool easier :)
setMinIdle(5);
setTestOnBorrow(true);
}
}
- 客戶端用法
package com.byedbl.pool.example1;
import org.junit.Test;
public class ConnPoolTest {
@Test
public void conn() throws Exception {
ConnPoolConfig connPoolConfig = new ConnPoolConfig();
connPoolConfig.setMinIdle(5);
connPoolConfig.setMaxIdle(8);
ConnPool connPool = new ConnPool(connPoolConfig);
Conn conn1 = connPool.borrowObject();
Conn conn2 = connPool.borrowObject();
Conn conn3 = connPool.borrowObject();
Conn conn4 = connPool.borrowObject();
Conn conn5 = connPool.borrowObject();
conn1.report();
connPool.returnObject(conn1);
conn2.report();
connPool.returnObject(conn2);
conn3.report();
connPool.returnObject(conn3);
conn4.report();
connPool.returnObject(conn4);
conn5.report();
connPool.returnObject(conn5);
conn5.report();
// 被歸還的對象的引用,不可以在次歸還
// java.lang.IllegalStateException: Object has already been retured to this pool or is invalid
try {
connPool.returnObject(conn5);
}catch (Exception e){
e.printStackTrace();
}
}
}
common-pool2連接池詳解與使用
common-pool2 使用
使用示例源碼
一個FTP的工具工程
thrift-client-manager
apache-common-pool2源碼分析
commons-pool2源碼分析