設(shè)計模式之對象池模式(Object Pool Pattern)
1 對象池模式的定義
對象池模式(Object Pool Pattern),是創(chuàng)建型設(shè)計模式的一種,將對象預(yù)先創(chuàng)建并初始化后放入對象池中,對象提供者就能利用已有的對象來處理請求,減少頻繁創(chuàng)建對象所占用的內(nèi)存空間和初始化時間。一個對象池包含一組已經(jīng)初始化并且可以使用的對象,可以在有需求時創(chuàng)建和銷毀對象。對象池的用戶可以從池子中取得對象,對其進行操作處理,并在不需要時歸還給池子而非直接銷毀。對象池是一個特殊的工廠對象,對象池模式就是單例模式加享元模式。
2 對象池模式的應(yīng)用場景
對象池模式主要適用于以下應(yīng)用場景。
(1)資源受限的場景。比如,不需要可伸縮性的環(huán)境(CPU\內(nèi)存等物理資源有限),CPU性能不夠強勁,內(nèi)存比較緊張,垃圾收集,內(nèi)存抖動會造成比較大的影響,需要提高內(nèi)存管理效率, 響應(yīng)性比吞吐量更為重要。
(2)在內(nèi)存中數(shù)量受限的對象。
(3)創(chuàng)建成本高的對象,可以考慮池化。
補充:常見的使用對象池的場景有在使用Socket時的各種連接池、線程池、數(shù)據(jù)庫連接池等。
3 對象池模式的UML類圖
對象池模式的UML類圖如下圖所示。
由上圖可以看到,對象池模式主要包含3個角色。
(1)對象池(ObjectPool):持有對象并提供取/還等方法。
(2)抽象池化對象(PooledObject):對池中對象的抽象。
(3)具體池化對象(ConcretePoolObject):對池中對象的封裝,封裝對象的狀態(tài)和一些其他信息。
4 對象池模式的通用寫法
以下是對象池模式的通用寫法。
- public class Client {
- public static void main(String[] args) {
- ObjectPool pool = new ObjectPool(10,50);
- IPooledObject object = pool.borrowObject();
- object.operation();
- pool.returnObject(object);
- System.out.println();
- }
- //抽象對象
- interface IPooledObject {
- void operation();
- }
- //具體對象
- static class ConcretePoolObject implements IPooledObject {
- public void operation() {
- System.out.println("doing");
- }
- }
- //對象池
- static class ObjectPool {
- private int step = 10; //當對象不夠用的時候,每次擴容的數(shù)量
- private int minCount;
- private int maxCount;
- private Vector<IPooledObject> returneds; //保存未借出的對象
- private Vector<IPooledObject> borroweds; //保存已被借出的對象
- //初始化對象池
- public ObjectPool(int minCount,int maxCount){
- borroweds = new Vector<IPooledObject>();
- returneds = new Vector<IPooledObject>();
- this.minCount = minCount;
- this.maxCount = maxCount;
- refresh(this.minCount);
- }
- //因為內(nèi)部狀態(tài)具備不變性,所以作為緩存的鍵
- public IPooledObject borrowObject() {
- IPooledObject next = null;
- if(returneds.size() > 0){
- Iterator<IPooledObject> i = returneds.iterator();
- while (i.hasNext()){
- next = i.next();
- returneds.remove(next);
- borroweds.add(next);
- return next;
- }
- }else{
- //計算出剩余可創(chuàng)建的對象數(shù)
- int count = (maxCount - minCount);
- //剩余可創(chuàng)建的數(shù)量大于單次固定創(chuàng)建的對象數(shù)
- //則再初始化一批固定數(shù)量的對象
- refresh(count > step ? step : count);
- }
- return next;
- }
- //不需要使用的對象歸還重復(fù)利用
- public void returnObject(IPooledObject pooledObject){
- returneds.add(pooledObject);
- if(borroweds.contains(pooledObject)){
- borroweds.remove(pooledObject);
- }
- }
- private void refresh(int count){
- for (int i = 0; i < count; i++) {
- returneds.add(new ConcretePoolObject());
- }
- }
- }
- }
對象池模式和享元模式的最大區(qū)別在于,對象池模式中會多一個回收對象重復(fù)利用的方法。所以,對象池模式應(yīng)該是享元模式更加具體的一個應(yīng)用場景。相當于先將對象從對象池中借出,用完之后再還回去,以此保證有限資源的重復(fù)利用。
5 對象池模式的優(yōu)點
復(fù)用池中對象,消除創(chuàng)建對象、回收對象所產(chǎn)生的內(nèi)存開銷、CPU開銷,以及跨網(wǎng)絡(luò)產(chǎn)生的網(wǎng)絡(luò)開銷。
6 對象池模式的缺點
(1)增加了分配/釋放對象的開銷。
(2)在并發(fā)環(huán)境中,多個線程可能(同時)需要獲取池中對象,進而需要在堆數(shù)據(jù)結(jié)構(gòu)上進行同步或者因為鎖競爭而產(chǎn)生阻塞,這種開銷要比創(chuàng)建銷毀對象的開銷高數(shù)百倍。
(3)由于池中對象的數(shù)量有限,勢必成為一個可伸縮性瓶頸。
(4)很難合理設(shè)定對象池的大小,如果太小,則起不到作用;如果過大,則占用內(nèi)存資源高。