1. 程式人生 > >【設計模式】單例模式

【設計模式】單例模式

amp 就會 保持 占用 back obj 線程不安全 only 大量

一、單例模式的定義

單例模式( Singleton Pattern) 是一個比較簡單的模式, 其定義如下:
Ensure a class has only one instance, and provide a global point of access to it.( 確保某一個類只有一個實例, 而且自行實例化並向整個系統提供這個實例。 )
單例類, 通過構造私有化確保了在一個應用中只產生一個實例, 並且是自行實例化的( 在Singleton中自己使用new Singleton()) 。

二、單例模式的實現

1、經典模式

class singleton
{
protected:
    singleton(){}
private: static singleton* p; public: static singleton* instance(); }; singleton* singleton::p = NULL; singleton* singleton::instance() { if (p == NULL) p = new singleton(); return p; }

這種模式不是線程安全的,如果線程a和線程b同時調用instance,那麽兩個線程都會創建一個示例,這明顯是不對的。

2、懶漢模式

不到萬不得已就不會去實例化類,也就是說在第一次用到類實例的時候才會去實例化,所以上邊的經典方法被歸為懶漢實現;

經過上面的演示,明顯懶漢模式是線程不安全的。那麽怎麽保證線程安全呢?加鎖!

加鎖的經典模式

class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
private:
    static singleton* p;
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};

pthread_mutex_t singleton::mutex;
singleton
* singleton::p = NULL; singleton* singleton::initance() { if (p == NULL) { pthread_mutex_lock(&mutex); if (p == NULL) p = new singleton(); pthread_mutex_unlock(&mutex); } return p; }

內部靜態變量的懶漢實現

此方法也很容易實現,在instance函數裏定義一個靜態的實例,也可以保證擁有唯一實例,在返回時只需要返回其指針就可以了。

class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};

pthread_mutex_t singleton::mutex;
singleton* singleton::initance()
{
    pthread_mutex_lock(&mutex);
    static singleton obj;
    pthread_mutex_unlock(&mutex);
    return &obj;
}

3、餓漢模式

餓了肯定要饑不擇食。所以在單例類定義的時候就進行實例化。

class singleton
{
protected:
    singleton()
    {}
private:
    static singleton* p;
public:
    static singleton* initance();
};

singleton* singleton::p = new singleton;
singleton* singleton::initance()
{
    return p;
}

為什麽說餓漢模式是線程安全的呢?因為靜態實例初始化在程序開始時進入主函數之前就由主線程以單線程方式完成了初始化,不必擔心多線程問題。

4、特點與選擇

由於要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,采用餓漢實現,可以實現更好的性能。這是以空間換時間。
在訪問量較小時,采用懶漢實現。這是以時間換空間。

三、優點與缺點

1、優點

由於單例模式在內存中只有一個實例, 減少了內存開支, 特別是一個對象需要頻繁地創建、 銷毀時, 而且創建或銷毀時性能又無法優化, 單例模式的優勢就非常明顯。
由於單例模式只生成一個實例, 所以減少了系統的性能開銷, 當一個對象的產生需要比較多的資源時, 如讀取配置、 產生其他依賴對象時, 則可以通過在應用啟動時直接產生一個單例對象, 然後用永久駐留內存的方式來解決。
單例模式可以避免對資源的多重占用, 例如一個寫文件動作, 由於只有一個實例存在內存中, 避免對同一個資源文件的同時寫操作。
單例模式可以在系統設置全局的訪問點, 優化和共享資源訪問, 例如可以設計一個單例類, 負責所有數據表的映射處理。

2、缺點

單例模式一般沒有接口, 擴展很困難, 若要擴展, 除了修改代碼基本上沒有第二種途徑可以實現。 單例模式為什麽不能增加接口呢? 因為接口對單例模式是沒有任何意義的, 它要求“自行實例化”, 並且提供單一實例、 接口或抽象類是不可能被實例化的。 當然, 在特殊情況下, 單例模式可以實現接口、 被繼承等, 需要在系統開發中根據環境判斷。
單例模式對測試是不利的。 在並行開發環境中, 如果單例模式沒有完成, 是不能進行測試的, 沒有接口也不能使用mock的方式虛擬一個對象。
單例模式與單一職責原則有沖突。 一個類應該只實現一個邏輯, 而不關心它是否是單例的, 是不是要單例取決於環境, 單例模式把“要單例”和業務邏輯融合在一個類中。

四、使用場景

在一個系統中, 要求一個類有且僅有一個對象, 如果出現多個對象就會出現“不良反應”, 可以采用單例模式, 具體的場景如下:
要求生成唯一序列號的環境;
在整個項目中需要一個共享訪問點或共享數據, 例如一個Web頁面上的計數器, 可以不用把每次刷新都記錄到數據庫中, 使用單例模式保持計數器的值, 並確保是線程安全的;
創建一個對象需要消耗的資源過多, 如要訪問IO和數據庫等資源;
需要定義大量的靜態常量和靜態方法( 如工具類) 的環境, 可以采用單例模式( 當然, 也可以直接聲明為static的方式) 。

【設計模式】單例模式