程序員的福音 - Apache Commons Pool

此文是系列文章第十三篇,前幾篇請點擊鏈接查看

程序猿的福音 - Apache Commons簡介

程序員的福音 - Apache Commons Lang

程序員的福音 - Apache Commons IO

程序員的福音 - Apache Commons Codec

程序員的福音 - Apache Commons Compress

程序員的福音 - Apache Commons Exec

程序員的福音 - Apache Commons Email

程序員的福音 - Apache Commons Net

程序員的福音 - 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)注。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容