簡介
單例模式(Singleton Pattern)保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
單例模式是一種對象創建型模式 (可參考 設計模式 創建型模式)。
結構
圖-單例模式結構圖
動機
在以下情況中,可以考慮應用單例模式:
- 保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
- 當唯一的實例應該對子類可擴展,並且用戶應該可以在不改變代碼的情況下使用擴展的實例。
實際應用場景
在計算機系統中,需要管理的資源包括軟件外部資源,譬如每台計算機可以有若干個打印機,但只能有一個Printer Spooler, 以避免兩個打印作業同時輸出到打印機中。
每台計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。任務管理器中難以啟動兩個相同的task。
要點
1、一個類只能有一個實例。
2、實例必須由類自行創建。
3、必須提供獲取實例的方法。
實例
1、懶漢式
instance 初始時沒有初始化,只有當第一次調 getInstance() 時才創建實例。
繼續向下執行,會生成兩個實例,違背了單例模式的初衷。
private LazySingleton() {
System.out.println("Singleton()");
}
private static LazySingleton instance = null;
public static LazySingleton getInstance() {
if ( null == instance) {
instance = new LazySingleton();
}
return instance;
}
}
2、餓漢式
餓漢根本等不及別人來找他,不管三七二十一先初始化了自身的實例,生怕自己餓着了。
類默認先直接初始化一個實例,以后調用 getInstance() 總是返回這個已創建好的實例。
缺點:在沒有必要獲取實例時,已經預先產生了開銷。
private HungerSinleton() {
System.out.println("Singleton()");
}
private static HungerSinleton instance = new HungerSinleton();
public static HungerSinleton getInstance() {
return instance;
}
}
3、雙重鎖的形式
如果既不想在沒有調用 getInstance() 方法時產生開銷,又不想發生線程安全問題,就可以采用雙重鎖的形式。
private SyncSingleton() {
System.out.println("Singleton()");
}
private static SyncSingleton instance = null;
public static SyncSingleton getInstance() {
if ( null == instance) {
synchronized(SyncSingleton. class) {
if ( null == instance) {
instance = new SyncSingleton();
}
}
}
return instance;
}
}
注:在外面判斷了instance實例是否存在,為什么在鎖定后又要在內部又判斷一次?
這是因為,如果 instance 為 null 時有兩個線程同時調用 getInstance(),由於 synchronized 機制,只有一個線程可以進入,另一個需要等待。
這時如果沒有第二道 instance 是否為 null 的判斷,就可能發生第一個線程創建一個實例,而第二個線程又創建一個實例的情況。
C++版單例模式
下面是一個采用餓漢式的例子
#include <iostream>
using namespace std;
class Singleton
{
private:
static Singleton *m_instance;
Singleton()
{
cout << " Singleton Construct " << endl;
}
public:
static Singleton* GetInstance()
{
return m_instance;
}
};
Singleton* Singleton::m_instance = new Singleton();
int main()
{
// Singleton *pSingletonA = new Singleton; // 編譯會報錯,因為不能訪問私有函數
Singleton *pSingletonA = Singleton::GetInstance();
Singleton *pSingletonB = Singleton::GetInstance();
if (pSingletonA == pSingletonB)
cout << " Same Instance " << endl;
return 0;
}