對象池模式(Object Pool Pattern)

語言: CN / TW / HK

本文節選自《設計模式就該這樣學》

1 對象池模式的定義

對象池模式(Object Pool Pattern),是創建型設計模式的一種,將對象預先創建並初始化後放入對象池中,對象提供者就能利用已有的對象來處理請求,減少頻繁創建對象所佔用的內存空間和初始化時間。 一個對象池包含一組已經初始化並且可以使用的對象,可以在有需求時創建和銷燬對象。對象池的用户可以從池子中取得對象,對其進行操作處理,並在不需要時歸還給池子而非直接銷燬。對象池是一個特殊的工廠對象,對象池模式就是單例模式加享元模式。

2 對象池模式的應用場景

對象池模式主要適用於以下應用場景。

(1)資源受限的場景。比如,不需要可伸縮性的環境(CPU\內存等物理資源有限),CPU性能不夠強勁,內存比較緊張,垃圾收集,內存抖動會造成比較大的影響,需要提高內存管理效率, 響應性比吞吐量更為重要。

(2)在內存中數量受限的對象。

(3)創建成本高的對象,可以考慮池化。

補充:常見的使用對象池的場景有在使用Socket時的各種連接池、線程池、數據庫連接池等。

3 對象池模式的UML類圖

對象池模式的UML類圖如下圖所示。

file

由上圖可以看到,對象池模式主要包含3個角色。

(1)對象池(ObjectPool):持有對象並提供取/還等方法。

(2)抽象池化對象(PooledObject):對池中對象的抽象。

(3)具體池化對象(ConcretePoolObject):對池中對象的封裝,封裝對象的狀態和一些其他信息。

4 對象池模式的通用寫法

以下是對象池模式的通用寫法。

```java

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;                      //當對象不夠用的時候,每次擴容的數量
    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);
    }

    //因為內部狀態具備不變性,所以作為緩存的鍵
    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{
            //計算出剩餘可創建的對象數
            int count = (maxCount - minCount);
            //剩餘可創建的數量大於單次固定創建的對象數
            //則再初始化一批固定數量的對象
            refresh(count > step ? step : count);
        }
        return next;
    }

    //不需要使用的對象歸還重複利用
    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());
        }
    }
}

}

```

對象池模式和享元模式的最大區別在於,對象池模式中會多一個回收對象重複利用的方法。所以,對象池模式應該是享元模式更加具體的一個應用場景。相當於先將對象從對象池中借出,用完之後再還回去,以此保證有限資源的重複利用。

5 對象池模式的優缺點

5.1 優點

複用池中對象,消除創建對象、回收對象所產生的內存開銷、CPU開銷,以及跨網絡產生的網絡開銷。

5.2 缺點

(1)增加了分配/釋放對象的開銷。

(2)在併發環境中,多個線程可能(同時)需要獲取池中對象,進而需要在堆數據結構上進行同步或者因為鎖競爭而產生阻塞,這種開銷要比創建銷燬對象的開銷高數百倍。

(3)由於池中對象的數量有限,勢必成為一個可伸縮性瓶頸。

(4)很難合理設定對象池的大小,如果太小,則起不到作用;如果過大,則佔用內存資源高。 關注『 Tom彈架構 』回覆“設計模式”可獲取完整源碼。

【推薦】Tom彈架構:30個設計模式真實案例(附源碼),挑戰年薪60W不是夢

本文為“Tom彈架構”原創,轉載請註明出處。技術在於分享,我分享我快樂!如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注『 Tom彈架構 』可獲取更多技術乾貨!