單例模式在多執行緒環境下確保「唯一實例」與「高效能」往往互為制約。本文系統梳理五種主流線程安全單例實現,從餓漢式到雙重檢查鎖(DCL),深度解析其核心原理、優缺點、適用場景及常見錯誤寫法,助你根據業務需求做出最佳架構決策。
public class Singleton {
// 類載入時即實例化,ClassLoader 機制天然保證線程安全
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
ExceptionInInitializerError)。public class Singleton {
private Singleton() {}
// 只有首次呼叫 getInstance() 才會載入 SingletonHolder,進而初始化 INSTANCE
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
readResolve)、被《Java Concurrency in Practice》推薦為標準寫法。public class Singleton {
private static Singleton instance = null;
private Singleton() {}
// 整個方法加鎖,每次呼叫都爭搶鎖
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// ❌ 錯誤示範:缺少 volatile,存在指令重排序風險
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 非原子操作:分配記憶體 -> 建構子 -> 賦值引用
}
}
}
return instance;
}
致命缺陷:JVM 允許指令重排序(分配記憶體 → 賦值引用 → 執行建構子),導致其他執行緒看到 instance != null 但物件尚未完全初始化,存取時拋 NPE。
public class Singleton {
// volatile 禁止指令重排序,保證「建構完成」先於「引用賦值」可見
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次檢查:無鎖快速路徑
synchronized (Singleton.class) { // 同步塊:僅首次建立爭搶
if (instance == null) { // 第二次檢查:防止重複建立
instance = new Singleton();
}
}
}
return instance;
}
}
volatile 建立 Happens-Before 關係,確保建構子執行完成對其他執行緒可見。VarHandle 替代;無法防反射/序列化破壞。上述所有基於建構子的方案均可被反射(Constructor.setAccessible(true))或序列化/反序列化破壞。唯一完美免疫的是枚舉單例:
public enum Singleton {
INSTANCE;
public void doWork() { /* ... */ }
}
| 方案 | 線程安全 | 延遲載入 | 效能(穩態) | 防反射 | 防序列化 | 程式碼複雜度 | 推薦指數 |
|---|---|---|---|---|---|---|---|
| 餓漢式 | ✅ | ❌ | ⭐⭐⭐⭐⭐ | ❌ | ❌ | 極低 | ⭐⭐⭐ |
| 靜態內部類 | ✅ | ✅ | ⭐⭐⭐⭐⭐ | ❌ | 需 readResolve | 低 | ⭐⭐⭐⭐⭐ |
| 同步方法 | ✅ | ✅ | ⭐ | ❌ | ❌ | 極低 | ⭐ |
| DCL (volatile) | ✅ | ✅ | ⭐⭐⭐⭐ | ❌ | 需 readResolve | 中 | ⭐⭐⭐⭐ |
| 枚舉 | ✅ | ❌ | ⭐⭐⭐⭐⭐ | ✅ | ✅ | 極低 | ⭐⭐⭐⭐⭐ |
getInstance() 中傳參、或維護舊專案已使用此模式。