此文是系列文章第十三篇,前幾篇請點(diǎn)擊鏈接查看
程序員的福音 - Apache Commons Compress
程序員的福音 - Apache Commons Collections
程序員的福音 - Apache Commons HttpClient
程序員的福音 - Apache Commons VFS(上)
程序員的福音 - Apache Commons VFS(下)
Apache Commons Pool 開源軟件庫提供了一個(gè)對象池 API 和許多對象池實(shí)現(xiàn)。
Pool 有兩個(gè)版本,目前主流的是 Pool2疾瓮,與 1.x 系列相比尘颓,Apache Commons Pool2 重新編寫了對象池實(shí)現(xiàn)佃声。除了性能和可伸縮性改進(jìn)之外菲嘴,版本2還包括健壯的實(shí)例跟蹤和對象池監(jiān)控虏辫。
后續(xù)文章出現(xiàn)的 Commons-Pool 指的就是 Pool2蚌吸。
Commons-Net目前最新版本是2.9.0,最低要求Java8以上砌庄。
maven坐標(biāo)如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
包結(jié)構(gòu)如下:
org.apache.commons.pool2org.apache.commons.pool2.implorg.apache.commons.pool2.proxy
下面簡單介紹一下其用法羹唠。
01. 簡介
為什么要有對象池呢,假如一個(gè)對象創(chuàng)建耗時(shí) 500 毫秒娄昆,而我們調(diào)用它的方法僅耗時(shí) 10 毫秒佩微,這種情況每次使用都 new 的話性價(jià)比很低,相當(dāng)于每次都要耗費(fèi) 550 毫秒萌焰。 對象池就是為了解決此類問題而誕生的哺眯,對于這些昂貴的對象來說,提前創(chuàng)建若干個(gè)對象用對象池管理起來扒俯,用的時(shí)候從對象池借來一個(gè)奶卓,用完后歸還 可以大大提升性能。
對象池是一種享元模式的實(shí)現(xiàn)撼玄,常用于各種連接池的實(shí)現(xiàn)夺姑。 比如我們常見的數(shù)據(jù)庫連接池 DBCP,Redis 客戶端 Jedis 等都依賴 Commons-Pool掌猛。
Commons-Pool 主要有三個(gè)角色:
PooledObject:池化對象盏浙,用于包裝實(shí)際的對象,提供一些附件的功能留潦。如 Commons-Pool 自帶的 DefaultPooledObject 會記錄對象的創(chuàng)建時(shí)間只盹,借用時(shí)間,歸還時(shí)間兔院,對象狀態(tài)等殖卑,PooledSoftReference 使用 Java 的軟引用來持有對象,便于 JVM 內(nèi)存不夠時(shí)回收對象坊萝。當(dāng)然我們也可以實(shí)現(xiàn) PooledObject 接口來定義我們自己的對象包裝器孵稽。
PooledObjectFactory:對象工廠,ObjectPool 對于每個(gè)對象的核心操作會代理給 PooledObjectFactory十偶。
需要一個(gè)新實(shí)例時(shí)菩鲜,就調(diào)用 makeObject 方法。
需要借用對象時(shí)會調(diào)用 activateObject 方法激活對象惦积,并且根據(jù)配置情況決定是否驗(yàn)證對象有效性接校,通過 validateObject 方法驗(yàn)證。
歸還對象時(shí)會調(diào)用 passivateObject 方法鈍化對象。
需要銷毀對象時(shí)候調(diào)用 destroyObject 方法蛛勉。
PooledObjectFactory 必須是線程安全的鹿寻。
ObjectPool :對象池接口,用于管理池中的所有對象诽凌,對于每個(gè)對象的操作會代理給 ObjectFactory毡熏。ObjectPool 有多個(gè)實(shí)現(xiàn),GenericObjectPool 提供了多種配置選項(xiàng)侣诵,包括限制空閑或活動(dòng)實(shí)例的數(shù)量痢法、在實(shí)例處于池中空閑時(shí)將其逐出等。從版本 2 開始杜顺,GenericObjectPool 還提供了廢棄實(shí)例跟蹤和刪除功能财搁。SoftReferenceObjectPool 可以根據(jù)需要增長,但允許垃圾收集器根據(jù)需要從池中逐出空閑實(shí)例躬络。
以下是部分類圖
02. 使用方式
Commons-Pool 用起來很簡單妇拯,下面我用一個(gè)例子簡單介紹下其用法
首先我們創(chuàng)建一個(gè)對象用于測試,對象構(gòu)造函數(shù)使用隨機(jī)延遲模擬創(chuàng)建的復(fù)雜
/**
* 復(fù)雜的對象洗鸵,創(chuàng)建出來比較耗時(shí)間
*/
public class ComplexObject {
private String name;
public ComplexObject(String name) {
try {
long t1 = System.currentTimeMillis();
// 模擬創(chuàng)建耗時(shí)操作
ThreadLocalRandom tlr = ThreadLocalRandom.current();
Thread.sleep(4000 + tlr.nextInt(2000));
long t2 = System.currentTimeMillis();
System.out.println(name + " 創(chuàng)建耗時(shí): " + (t2-t1) + "ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
其次創(chuàng)建一個(gè) ComplexPooledObjectFactory 實(shí)現(xiàn)。當(dāng)我們有復(fù)雜的操作仗嗦,比如激活對象膘滨,鈍化對象,銷毀對象(需要釋放資源)等就需要實(shí)現(xiàn) PooledObjectFactory 來定制稀拐,如果沒有這些操作選擇繼承 BasePooledObjectFactory 抽象類更方便火邓。下面分別給出兩種創(chuàng)建的代碼示例
1. 繼承 BasePooledObjectFactory
public class SimplePooledObjectFactory extends BasePooledObjectFactory<ComplexObject> {
@Override
public ComplexObject create() {
// 隨機(jī)指定一個(gè)名稱,用于區(qū)分ComplexObject
String name = "test" + ThreadLocalRandom.current().nextInt(100);
return new ComplexObject(name);
}
@Override
public PooledObject<ComplexObject> wrap(ComplexObject obj) {
// 使用默認(rèn)池化對象包裝ComplexObject
return new DefaultPooledObject(obj);
}
}
2. 實(shí)現(xiàn) PooledObjectFactory
public class ComplexPooledObjectFactory implements PooledObjectFactory<ComplexObject> {
@Override
public PooledObject<ComplexObject> makeObject() {
// 隨機(jī)指定一個(gè)名稱德撬,用于區(qū)分ComplexObject并使用默認(rèn)池化對象包裝ComplexObject
String name = "test" + ThreadLocalRandom.current().nextInt(100);
return new DefaultPooledObject<>(new ComplexObject(name));
}
@Override
public void destroyObject(PooledObject<ComplexObject> p) {
// 銷毀對象铲咨,當(dāng)清空,空閑對象大于配置值等會銷毀多余對象
// 此處應(yīng)釋放掉對象占用的資源蜓洪,如關(guān)閉連接纤勒,關(guān)閉IO等
}
@Override
public boolean validateObject(PooledObject<ComplexObject> p) {
// 驗(yàn)證對象狀態(tài)是否正常,是否可用
return true;
}
@Override
public void activateObject(PooledObject<ComplexObject> p) {
// 激活對象隆檀,使其可用
}
@Override
public void passivateObject(PooledObject<ComplexObject> p) {
// 鈍化對象摇天,使其不可用
}
}
最后編寫測試代碼
public static void main(String[] args) throws Exception {
// 創(chuàng)建配置對象
GenericObjectPoolConfig<ComplexObject> poolConfig = new GenericObjectPoolConfig<>();
// 最大空閑實(shí)例數(shù),空閑超過此值將會被銷毀淘汰
poolConfig.setMaxIdle(5);
// 最大對象數(shù)量恐仑,包含借出去的和空閑的
poolConfig.setMaxTotal(20);
// 最小空閑實(shí)例數(shù)泉坐,對象池將至少保留2個(gè)空閑對象
poolConfig.setMinIdle(2);
// 對象池滿了,是否阻塞獲壬哑汀(false則借不到直接拋異常)
poolConfig.setBlockWhenExhausted(true);
// BlockWhenExhausted為true時(shí)生效腕让,對象池滿了阻塞獲取超時(shí),不設(shè)置則阻塞獲取不超時(shí)歧斟,也可在borrowObject方法傳遞第二個(gè)參數(shù)指定本次的超時(shí)時(shí)間
poolConfig.setMaxWaitMillis(3000);
// 創(chuàng)建對象后是否驗(yàn)證對象纯丸,調(diào)用objectFactory#validateObject
poolConfig.setTestOnCreate(false);
// 借用對象后是否驗(yàn)證對象 validateObject
poolConfig.setTestOnBorrow(true);
// 歸還對象后是否驗(yàn)證對象 validateObject
poolConfig.setTestOnReturn(true);
// 每30秒定時(shí)檢查淘汰多余的對象, 啟用單獨(dú)的線程處理
poolConfig.setTimeBetweenEvictionRunsMillis(1000 * 60 * 30);
// 每30秒定時(shí)檢查期間是否驗(yàn)證對象 validateObject
poolConfig.setTestWhileIdle(false);
// jmx監(jiān)控偏形,和springboot自帶的jmx沖突,可以選擇關(guān)閉此配置或關(guān)閉springboot的jmx配置
poolConfig.setJmxEnabled(false);
ComplexPooledObjectFactory objectFactory = new ComplexPooledObjectFactory();
GenericObjectPool<ComplexObject> objectPool = new GenericObjectPool<>(objectFactory, poolConfig);
// 申請對象
ComplexObject obj1 = objectPool.borrowObject();
println("第一次申請對象:" + obj1.getName());
// returnObject應(yīng)該放在finally中 避免業(yè)務(wù)異常沒有歸還對象液南,demo僅做示例
objectPool.returnObject(obj1);
// 申請對象壳猜, 由于之前歸還了,借用的還是之前的對象
ComplexObject obj2 = objectPool.borrowObject();
println("第二次申請對象:" + obj2.getName());
// 再次申請對象滑凉,由于之前沒有歸還统扳,借用的是新創(chuàng)建的
ComplexObject obj3 = objectPool.borrowObject();
println("第三次申請對象:" + obj3.getName());
// returnObject應(yīng)該放在finally中 避免業(yè)務(wù)異常沒有歸還對象,demo僅做示例
objectPool.returnObject(obj2);
objectPool.returnObject(obj3);
}
運(yùn)行結(jié)果如下
test41 創(chuàng)建耗時(shí): 5400ms
第一次申請對象:test41
第二次申請對象:test41
test58 創(chuàng)建耗時(shí): 5349ms
第三次申請對象:test58
當(dāng)然如果借用次數(shù)越多畅姊,節(jié)省下來的時(shí)間就越多咒钟。
由于示例比較簡單粗暴,在對象池剛剛創(chuàng)建還沒提前創(chuàng)建好對象若未,我們就去使用了朱嘴,所以效果不是很理想,正常使用效果會比較好粗合。
03. KeyedObjectPool
Commons-Pool 還有 KeyedPooledObjectFactory萍嬉,KeyedObjectPool 接口,它支持 Key Value 形式隙疚。
public interface KeyedPooledObjectFactory<K, V> {
// 通過參數(shù)創(chuàng)建對象
PooledObject<V> makeObject(K key);
// 通過參數(shù)激活對象壤追,使其可用
void activateObject(K key, PooledObject<V> obj);
// 通過參數(shù)鈍化對象,使其不可用
void passivateObject(K key, PooledObject<V> obj);
// 通過參數(shù)驗(yàn)證對象狀態(tài)是否正常供屉,是否可用
boolean validateObject(K key, PooledObject<V> obj);
// 通過參數(shù)銷毀對象行冰,當(dāng)清空,空閑對象大于配置值等會銷毀多余對象
// 此處應(yīng)釋放掉對象占用的資源伶丐,如關(guān)閉連接悼做,關(guān)閉IO等
void destroyObject(K key, PooledObject<V> obj);
}
具體使用方式就不做介紹了,用法和第二節(jié)的類似哗魂,區(qū)別是對象借用和歸還操作需要額外傳遞自定義的 Key 參數(shù)肛走。
04. 總結(jié)
Commons-Pool 作為對象池工具包,支持對象的管理啡彬、跟蹤和監(jiān)控羹与,并且支持自定義池化對象來擴(kuò)展對象管理的行為,如果有相關(guān)需求可以使用庶灿。
后續(xù)章節(jié)我將繼續(xù)給大家介紹 commons 中其他好用的工具類庫纵搁,期待你的關(guān)注。