首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Apache Commons Pool2 實現對象池化技術:從工廠到池的完整實戰

     編輯:Java 相關知識

頻繁創建銷毀昂貴對象(數據庫連接、線程、HTTP 客戶端、大對象)會造成 GC 壓力與性能抖動。Apache Commons Pool2 提供生產級對象池實現,本文演示 BasePooledObjectFactory 自定義工廠、GenericObjectPool 配置參數、借還對象流程、關鍵配置(最大閒置、驗證、驅逐策略)及監控指標。

一、核心類結構


ObjectPool (介面)
    ↑
GenericObjectPool (通用實現,線程安全)
    ↑
BasePooledObjectFactory (抽象工廠,需子類實現 create/wrap)
    ↑
YourFactory extends BasePooledObjectFactory

二、自定義對象工廠


public class ComputerFactory extends BasePooledObjectFactory {
    @Override
    public Computer create() throws Exception {
        // 昂貴對象創建邏輯:建立連接、加載配置、預熱
        return new Computer("MacBook Pro", "M2", 16);
    }

    @Override
    public PooledObject wrap(Computer obj) {
        return new DefaultPooledObject<>(obj); // 包裝為池管理對象
    }

    // 可選覆寫:激活/鈍化/驗證/銷毀
    @Override
    public void activateObject(PooledObject p) throws Exception {
        // 從閒置狀態激活:重置狀態、重新綁定資源
        p.getObject().reset();
    }

    @Override
    public void passivateObject(PooledObject p) throws Exception {
        // 歸還閒置前鈍化:清理臨時狀態、釋放非核心資源
        p.getObject().clearCache();
    }

    @Override
    public boolean validateObject(PooledObject p) {
        // 借出前驗證對象可用性
        return p.getObject().isHealthy();
    }

    @Override
    public void destroyObject(PooledObject p) throws Exception {
        // 銷毀:關閉連接、釋放原生資源
        p.getObject().shutdown();
    }

    public static class Computer {
        private final String model;
        private final String cpu;
        private final int ramGb;
        private boolean healthy = true;

        public Computer(String model, String cpu, int ramGb) {
            this.model = model; this.cpu = cpu; this.ramGb = ramGb;
            // 模擬昂貴初始化
        }
        public void reset() { /* 重置狀態 */ }
        public void clearCache() { /* 清理緩存 */ }
        public boolean isHealthy() { return healthy; }
        public void shutdown() { healthy = false; /* 釋放資源 */ }
        @Override public String toString() { return "Computer[" + model + "]"; }
    }
}

三、創建並配置對象池


public class PoolDemo {
    public static void main(String[] args) throws Exception {
        // 1. 創建工廠
        ComputerFactory factory = new ComputerFactory();

        // 2. 配置池參數
        GenericObjectPoolConfig config = new GenericObjectPoolConfig<>();
        config.setMaxTotal(20);                 // 最大對象數 (活躍+閒置)
        config.setMaxIdle(10);                  // 最大閒置對象數
        config.setMinIdle(2);                   // 最小閒置對象數 (啟動預熱)
        config.setMaxWaitMillis(3000);          // 借對象最大等待時間
        config.setTestOnBorrow(true);           // 借出前驗證
        config.setTestOnReturn(false);          // 歸還時不驗證 (性能)
        config.setTestWhileIdle(true);          // 閒置驅逐器驗證
        config.setTimeBetweenEvictionRunsMillis(30000); // 驅逐器運行間隔 30s
        config.setMinEvictableIdleTimeMillis(60000);    // 閒置超過 60s 可被驅逐
        config.setBlockWhenExhausted(true);     // 用盡時阻塞等待

        // 3. 創建池
        GenericObjectPool pool = 
            new GenericObjectPool<>(factory, config);

        // 4. 使用示例:多線程借還
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                ComputerFactory.Computer c = null;
                try {
                    c = pool.borrowObject(); // 獲取對象
                    System.out.println(Thread.currentThread().getName() + " 借到: " + c);
                    Thread.sleep(100); // 模擬業務處理
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (c != null) pool.returnObject(c); // 必須歸還
                }
            }, "Worker-" + i).start();
        }

        // 5. 監控指標
        Thread.sleep(2000);
        System.out.println("活躍: " + pool.getNumActive() + 
                           ", 閒置: " + pool.getNumIdle() +
                           ", 等待線程: " + pool.getNumWaiters());

        // 6. 關閉池 (釋放所有資源)
        pool.close();
    }
}

四、關鍵配置參數詳解

參數默認值建議生產值說明
maxTotal8根據 CPU/IO/DB 連接數上限設置,如 20-100池中最大對象總數
maxIdle8maxTotal * 0.5 ~ 0.8最大閒置對象,超過會被驅逐
minIdle02-5 (預熱)最小閒置,啟動時預創建
maxWaitMillis-1 (無限等待)3000-5000 ms借對象超時時間,防止線程無限掛起
testOnBorrowfalsetrue借出前 validateObject,保證可用性
testWhileIdlefalsetrue閒置驅逐時驗證,清理失效對象
timeBetweenEvictionRunsMillis-1 (不運行)30000-60000 ms驅逐器掃描間隔
minEvictableIdleTimeMillis1800000 (30分)60000-300000 ms閒置超過此時間可被驅逐

五、避坑指南

  1. 必須在 finally 中歸還:pool.returnObject(obj),否則對象泄漏導致池耗盡。
  2. 驗證失敗處理:validateObject 返回 false 會銷毀對象並重新創建,需確保創建邏輯冪等。
  3. 狀態重置:activateObject/passivateObject 必須徹底清理業務狀態,防止髒數據跨請求污染。
  4. 關閉池:應用關閉時調用 pool.close(),觸發 destroyObject 釋放底層資源(連接、句柄、內存映射)。
  5. 監控告警:導出 getNumActive()、getNumIdle()、getNumWaiters() 到 Prometheus/Grafana,設置「活躍接近 maxTotal」告警。

六、擴展:Spring 整合


@Bean
public GenericObjectPool computerPool() {
    GenericObjectPoolConfig config = new GenericObjectPoolConfig<>();
    config.setMaxTotal(20);
    config.setTestOnBorrow(true);
    return new GenericObjectPool<>(new ComputerFactory(), config);
}

// 使用
@Autowired GenericObjectPool pool;
ComputerFactory.Computer c = pool.borrowObject();
try { /* 業務 */ } finally { pool.returnObject(c); }
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved