什麼是單例?
單例是可應用於類別的最簡單的設計模式之一。人們有時會說“這個類別是單例”,意思是這個類別實現了單例設計模式。有時需要編寫一個只能建立一個物件的類別。例如,負責記錄或連接資料庫的類別。單例設計模式描述了我們如何完成這樣的任務。單例是一種做兩件事的設計模式:-
保證一個類別只有一個該類別的實例。
-
提供對該類別的實例的全域存取點。
-
私有構造函數。限制在類別本身之外創建類別物件的能力。
-
傳回類別的實例的公共靜態方法。這種方法稱為
getInstance
. 這是類別實例的全域存取點。
實施方案
單例設計模式有不同的使用方式。每個選項都有其自身的優點和缺點。在這裡,一如既往:沒有理想,但需要為此奮鬥。但首先,讓我們先定義什麼是好的,什麼是壞的,以及哪些指標會影響設計模式的實現評估。讓我們從正面的方面開始。以下是賦予實施多汁性和吸引力的標準:-
延遲初始化:當應用程式恰好在需要時載入類別時。
-
程式碼的簡單性和透明度:當然,衡量標準是主觀的,但很重要。
-
線程安全:在多線程環境下正常工作。
-
多執行緒環境中的高效能:共享資源時,執行緒之間的阻塞最少或根本不阻塞。
-
非惰性初始化:當應用程式啟動時載入一個類別,無論是否需要它(一個悖論,在IT世界中,最好是惰性的)
-
程式碼複雜且可讀性差。此指標也是主觀的。我們假設如果血液來自眼睛,那麼實施效果一般。
-
缺乏線程安全。換句話說,「線程危險」。多執行緒環境下操作不正確。
-
多執行緒環境中的效能較差:執行緒在共享資源時始終或經常相互阻塞。
程式碼
現在我們準備考慮各種實施選項,列出優點和缺點:簡單的解決方案
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
最簡單的實作。優點:
-
程式碼簡單透明
-
線程安全
-
多執行緒環境下的高效能
- 不是惰性初始化。
延遲初始化
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
優點:
-
延遲初始化。
-
不是線程安全的
同步存取器
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
優點:
-
延遲初始化。
-
線程安全
-
多線程環境下效能不佳
getInstance
是同步的,一次只能輸入一個。事實上,我們不需要同步整個方法,而只需要同步初始化新類別物件的部分。但我們不能簡單地將synchronized
負責創建新物件的部分包裝在區塊中:這不會提供線程安全性。情況有點複雜。正確的同步方法如下:
雙重檢查鎖定
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
優點:
-
延遲初始化。
-
線程安全
-
多執行緒環境下的高效能
-
低於 1.5 的 Java 版本不支援(1.5 版中修復了 volatile 關鍵字)
INSTANCE
必須是final
, 或volatile
。我們今天要討論的最後一個實現是Class Holder Singleton
.
類別持有者單例
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
優點:
-
延遲初始化。
-
線程安全。
-
多線程環境下的高效能。
-
為了正確運行,需要確保類別物件的
Singleton
初始化沒有錯誤。否則,第一個方法呼叫getInstance
將以錯誤結束ExceptionInInitializerError
,並且所有後續方法呼叫都將失敗NoClassDefFoundError
。
單例模式的優點和缺點
一般來說,單例完全按照預期執行:-
保證一個類別只有一個該類別的實例。
-
提供對該類別的實例的全域存取點。
-
Singleton 違反了 SRP(單一職責原則)——Singleton 類別除了其直接職責外,還控制其副本數量。
-
常規類別或方法對單例的依賴在類別的公共契約中不可見。
-
全域變數是不好的。單例最終會變成一個巨大的全域變數。
-
單例的存在通常降低了應用程式的可測試性,特別是使用單例的類別的可測試性。
GO TO FULL VERSION