此文是系列文章第十三篇,前幾篇請點擊鏈接查看
程序員的福音 - Apache Commons Compress
程序員的福音 - Apache Commons Collections
程序員的福音 - Apache Commons HttpClient
程序員的福音 - Apache Commons VFS(上)
程序員的福音 - Apache Commons VFS(下)
Apache Commons Pool 開源軟件庫提供了一個對象池 API 和許多對象池實現(xiàn)。
Pool 有兩個版本,目前主流的是 Pool2,與 1.x 系列相比,Apache Commons Pool2 重新編寫了對象池實現(xiàn)。除了性能和可伸縮性改進之外,版本2還包括健壯的實例跟蹤和對象池監(jiān)控。
后續(xù)文章出現(xiàn)的 Commons-Pool 指的就是 Pool2。
Commons-Net目前最新版本是2.9.0,最低要求Java8以上。
maven坐標如下:
<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. 簡介
為什么要有對象池呢,假如一個對象創(chuàng)建耗時 500 毫秒,而我們調(diào)用它的方法僅耗時 10 毫秒,這種情況每次使用都 new 的話性價比很低,相當于每次都要耗費 550 毫秒。 對象池就是為了解決此類問題而誕生的,對于這些昂貴的對象來說,提前創(chuàng)建若干個對象用對象池管理起來,用的時候從對象池借來一個,用完后歸還 可以大大提升性能。
對象池是一種享元模式的實現(xiàn),常用于各種連接池的實現(xiàn)。 比如我們常見的數(shù)據(jù)庫連接池 DBCP,Redis 客戶端 Jedis 等都依賴 Commons-Pool。
Commons-Pool 主要有三個角色:
PooledObject:池化對象,用于包裝實際的對象,提供一些附件的功能。如 Commons-Pool 自帶的 DefaultPooledObject 會記錄對象的創(chuàng)建時間,借用時間,歸還時間,對象狀態(tài)等,PooledSoftReference 使用 Java 的軟引用來持有對象,便于 JVM 內(nèi)存不夠時回收對象。當然我們也可以實現(xiàn) PooledObject 接口來定義我們自己的對象包裝器。
PooledObjectFactory:對象工廠,ObjectPool 對于每個對象的核心操作會代理給 PooledObjectFactory。
需要一個新實例時,就調(diào)用 makeObject 方法。
需要借用對象時會調(diào)用 activateObject 方法激活對象,并且根據(jù)配置情況決定是否驗證對象有效性,通過 validateObject 方法驗證。
歸還對象時會調(diào)用 passivateObject 方法鈍化對象。
需要銷毀對象時候調(diào)用 destroyObject 方法。
PooledObjectFactory 必須是線程安全的。
ObjectPool :對象池接口,用于管理池中的所有對象,對于每個對象的操作會代理給 ObjectFactory。ObjectPool 有多個實現(xiàn),GenericObjectPool 提供了多種配置選項,包括限制空閑或活動實例的數(shù)量、在實例處于池中空閑時將其逐出等。從版本 2 開始,GenericObjectPool 還提供了廢棄實例跟蹤和刪除功能。SoftReferenceObjectPool 可以根據(jù)需要增長,但允許垃圾收集器根據(jù)需要從池中逐出空閑實例。
以下是部分類圖
02. 使用方式
Commons-Pool 用起來很簡單,下面我用一個例子簡單介紹下其用法
首先我們創(chuàng)建一個對象用于測試,對象構(gòu)造函數(shù)使用隨機延遲模擬創(chuàng)建的復雜
/**
* 復雜的對象,創(chuàng)建出來比較耗時間
*/
public class ComplexObject {
private String name;
public ComplexObject(String name) {
try {
long t1 = System.currentTimeMillis();
// 模擬創(chuàng)建耗時操作
ThreadLocalRandom tlr = ThreadLocalRandom.current();
Thread.sleep(4000 + tlr.nextInt(2000));
long t2 = System.currentTimeMillis();
System.out.println(name + " 創(chuàng)建耗時: " + (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)建一個 ComplexPooledObjectFactory 實現(xiàn)。當我們有復雜的操作,比如激活對象,鈍化對象,銷毀對象(需要釋放資源)等就需要實現(xiàn) PooledObjectFactory 來定制,如果沒有這些操作選擇繼承 BasePooledObjectFactory 抽象類更方便。下面分別給出兩種創(chuàng)建的代碼示例
1. 繼承 BasePooledObjectFactory
public class SimplePooledObjectFactory extends BasePooledObjectFactory<ComplexObject> {
@Override
public ComplexObject create() {
// 隨機指定一個名稱,用于區(qū)分ComplexObject
String name = "test" + ThreadLocalRandom.current().nextInt(100);
return new ComplexObject(name);
}
@Override
public PooledObject<ComplexObject> wrap(ComplexObject obj) {
// 使用默認池化對象包裝ComplexObject
return new DefaultPooledObject(obj);
}
}
2. 實現(xiàn) PooledObjectFactory
public class ComplexPooledObjectFactory implements PooledObjectFactory<ComplexObject> {
@Override
public PooledObject<ComplexObject> makeObject() {
// 隨機指定一個名稱,用于區(qū)分ComplexObject并使用默認池化對象包裝ComplexObject
String name = "test" + ThreadLocalRandom.current().nextInt(100);
return new DefaultPooledObject<>(new ComplexObject(name));
}
@Override
public void destroyObject(PooledObject<ComplexObject> p) {
// 銷毀對象,當清空,空閑對象大于配置值等會銷毀多余對象
// 此處應釋放掉對象占用的資源,如關(guān)閉連接,關(guān)閉IO等
}
@Override
public boolean validateObject(PooledObject<ComplexObject> p) {
// 驗證對象狀態(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ù),空閑超過此值將會被銷毀淘汰
poolConfig.setMaxIdle(5);
// 最大對象數(shù)量,包含借出去的和空閑的
poolConfig.setMaxTotal(20);
// 最小空閑實例數(shù),對象池將至少保留2個空閑對象
poolConfig.setMinIdle(2);
// 對象池滿了,是否阻塞獲?。╢alse則借不到直接拋異常)
poolConfig.setBlockWhenExhausted(true);
// BlockWhenExhausted為true時生效,對象池滿了阻塞獲取超時,不設(shè)置則阻塞獲取不超時,也可在borrowObject方法傳遞第二個參數(shù)指定本次的超時時間
poolConfig.setMaxWaitMillis(3000);
// 創(chuàng)建對象后是否驗證對象,調(diào)用objectFactory#validateObject
poolConfig.setTestOnCreate(false);
// 借用對象后是否驗證對象 validateObject
poolConfig.setTestOnBorrow(true);
// 歸還對象后是否驗證對象 validateObject
poolConfig.setTestOnReturn(true);
// 每30秒定時檢查淘汰多余的對象, 啟用單獨的線程處理
poolConfig.setTimeBetweenEvictionRunsMillis(1000 * 60 * 30);
// 每30秒定時檢查期間是否驗證對象 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應該放在finally中 避免業(yè)務異常沒有歸還對象,demo僅做示例
objectPool.returnObject(obj1);
// 申請對象, 由于之前歸還了,借用的還是之前的對象
ComplexObject obj2 = objectPool.borrowObject();
println("第二次申請對象:" + obj2.getName());
// 再次申請對象,由于之前沒有歸還,借用的是新創(chuàng)建的
ComplexObject obj3 = objectPool.borrowObject();
println("第三次申請對象:" + obj3.getName());
// returnObject應該放在finally中 避免業(yè)務異常沒有歸還對象,demo僅做示例
objectPool.returnObject(obj2);
objectPool.returnObject(obj3);
}
運行結(jié)果如下
test41 創(chuàng)建耗時: 5400ms
第一次申請對象:test41
第二次申請對象:test41
test58 創(chuàng)建耗時: 5349ms
第三次申請對象:test58
當然如果借用次數(shù)越多,節(jié)省下來的時間就越多。
由于示例比較簡單粗暴,在對象池剛剛創(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ù)驗證對象狀態(tài)是否正常,是否可用
boolean validateObject(K key, PooledObject<V> obj);
// 通過參數(shù)銷毀對象,當清空,空閑對象大于配置值等會銷毀多余對象
// 此處應釋放掉對象占用的資源,如關(guān)閉連接,關(guān)閉IO等
void destroyObject(K key, PooledObject<V> obj);
}
具體使用方式就不做介紹了,用法和第二節(jié)的類似,區(qū)別是對象借用和歸還操作需要額外傳遞自定義的 Key 參數(shù)。
04. 總結(jié)
Commons-Pool 作為對象池工具包,支持對象的管理、跟蹤和監(jiān)控,并且支持自定義池化對象來擴展對象管理的行為,如果有相關(guān)需求可以使用。
后續(xù)章節(jié)我將繼續(xù)給大家介紹 commons 中其他好用的工具類庫,期待你的關(guān)注。