對象池的定義:
對象的實例化是最耗費性能的操作之一,這在過去是個大問題,現在不用再過分關注它。但當我們處理封裝外部資源的對象(如數據庫連接)時,對象的創建操作則會耗費很多資源。
解決方案是 重用和 共享這些創建成本高昂的對象,這稱為 對象池模式(創建型模式)。
解決方案是 重用和 共享這些創建成本高昂的對象,這稱為 對象池模式(創建型模式)。
直接上代碼:
1、對象工廠類
package com.zc.demo; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; public class SeatNumFactory implements PooledObjectFactory<SeatNum> { // 創建對象 或 引用現有對象 @Override public PooledObject<SeatNum> makeObject() throws Exception { System.out.println("【創建對象】"); return new DefaultPooledObject<SeatNum>(new SeatNum()); } // 銷毀對象 @Override public void destroyObject(PooledObject<SeatNum> pooledObject) throws Exception { System.out.println("【銷毀對象】,剩余數量="+pooledObject.getObject().num); pooledObject.deallocate();// 銷毀 } // 驗證對象 @Override public boolean validateObject(PooledObject<SeatNum> pooledObject) { System.out.println("【驗證對象】數量="+pooledObject.getObject().num); return pooledObject.getObject().num > 0; // 對象的一個銷毀條件 } // 活動對象 @Override public void activateObject(PooledObject<SeatNum> pooledObject) throws Exception { System.out.println("【活動對象】初始化前剩余數量="+pooledObject.getObject().num); // pooledObject.getObject().num = 100; // System.out.println("【活動對象】初始化后剩余數量="+pooledObject.getObject().num); } // 停用(歸還)對象 @Override public void passivateObject(PooledObject<SeatNum> pooledObject) throws Exception { System.out.println("【停用對象】數量="+pooledObject.getObject().num); } }
2、對象類
package com.zc.demo; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public class SeatNum { int num = 100; void doSomething() { System.out.println("引用前的座位數量:" + this.num); num -= 40; System.out.println("引用后的座位數量:" + this.num); } public static void main(String[] args) { GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig(); genericObjectPoolConfig.setMaxTotal(5); genericObjectPoolConfig.setMinIdle(2);//最小空閑數量,也是默認初始化的數量 genericObjectPoolConfig.setMaxIdle(2); genericObjectPoolConfig.setMinEvictableIdleTimeMillis(1000); genericObjectPoolConfig.setTestOnBorrow(true);// 引用對象后(對象已存在,且重復使用)調用驗證validateObject(常用) // genericObjectPoolConfig.setTestOnReturn(true);// 停用對象前(對象已存在)調用驗證validateObject(常用) // genericObjectPoolConfig.setTestOnCreate(true);// 創建對象(對象未存在)時驗證validateObject(極少情況采用) // genericObjectPoolConfig.setTestWhileIdle(true);// 對象一直空閑時驗證validateObject(極少情況采用) GenericObjectPool<SeatNum> objectPool = new GenericObjectPool<>(new SeatNumFactory(), genericObjectPoolConfig); SeatNum powerBank = null; for (int i = 0; i < 10; i++) { try { objectPool.preparePool();// 默認初始化 System.out.println("====================【" + i + "】==================="); powerBank = objectPool.borrowObject(); powerBank.doSomething(); } catch (Exception e) { e.printStackTrace(); } finally { if (powerBank != null) { objectPool.returnObject(powerBank); } } } } }
對象池的優點:
(1)復用池中對象
(2)消除創建對象、回收對象 所產生的內存開銷、cpu開銷以及(若跨網絡)產生的網絡開銷.
對象池的缺點:
(1)現在Java的對象分配操作不比c語言的malloc調用慢, 對於輕中量級的對象, 分配/釋放對象的開銷可以忽略不計;
(2)並發環境中, 多個線程可能(同時)需要獲取池中對象, 進而需要在堆數據結構上進行同步或者因為鎖競爭而產生阻塞, 這種開銷要比創建銷毀對象的開銷高數百倍;
(3)由於池中對象的數量有限, 勢必成為一個可伸縮性瓶頸;
(4)很難正確的設定對象池的大小, 如果太小則起不到作用, 如果過大, 則占用內存資源高, 可以起一個線程定期掃描分析, 將池壓縮到一個合適的尺寸以節約內存,但為了獲得不錯的分析結果, 在掃描期間可能需要暫停復用以避免干擾(造成效率低下), 或者使用非常復雜的算法策略(增加維護難度);
(5)設計和使用對象池容易出錯, 設計上需要注意狀態同步, 這是個難點, 使用上可能存在忘記歸還(就像c語言編程忘記free一樣), 重復歸還(可能需要做個循環判斷一下是否池中存在此對象, 這也是個開銷), 歸還后仍舊使用對象(可能造成多個線程並發使用一個對象的情況)等問題;