確保一個類全局僅有一個實例,並提供全局訪問點。構造器私有化,靜態工廠方法返回唯一實例。
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 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;
}
問題:instance = new Singleton() 非原子操作(分配內存 → 初始化對象 → 賦值引用),JVM 可能重排序為「分配 → 賦值 → 初始化」。線程 A 執行到賦值時,instance 非 null 但對象未初始化完成,線程 B 進入首次判斷見非 null 直接返回,導致使用半初始化對象。
public class Singleton {
private static volatile Singleton instance; // 關鍵:禁止指令重排
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次檢查(無鎖,性能)
synchronized (Singleton.class) { // 加鎖
if (instance == null) { // 第二次檢查(防止重複創建)
instance = new Singleton(); // volatile 保證初始化可見性
}
}
}
return instance;
}
}
volatile 禁止指令重排序,確保「初始化完成」先於「引用賦值」對其他線程可見。
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}
Holder 類僅在首次調用 getInstance() 時加載。volatile,最推薦。
public enum Singleton {
INSTANCE;
public void doSomething() { ... }
}
| 寫法 | 線程安全 | 延遲加載 | 性能 | 推薦指數 |
|---|---|---|---|---|
| 餓漢式 | ✅ | ❌ | 極高 | ⭐⭐⭐ (常用、簡單場景) |
| 靜態內部類 | ✅ | ✅ | 極高 | ⭐⭐⭐⭐⭐ (通用首選) |
| 枚舉 | ✅ | ❌ | 極高 | ⭐⭐⭐⭐ (需防序列化/反射) |
| DCL + volatile | ✅ | ✅ | 高 | ⭐⭐⭐ (教學理解、老代碼維護) |
| 同步方法 | ✅ | ✅ | 低 | ⭐ (不推薦) |
setAccessible(true) 繞過。枚舉單例免疫(JVM 保證)。防御:構造器中檢查 if (instance != null) throw new IllegalStateException()。Serializable 且無 readResolve() 會創建新對象。防御:private Object readResolve() { return instance; }。枚舉單例免疫。