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() { ... }
線程安全,但鎖粒度過大,實例化後每次調用仍同步,性能差。
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) instance = new Singleton();
}
}
return instance;
}
問題:instance = new Singleton() 非原子,JVM 指令重排序可能導致:
1. 分配內存 → 2. 初始化對象 → 3. 引用指向內存
重排為 1→3→2 時,其他線程見 instance != null 卻拿到半初始化對象。
private static volatile Singleton instance;
volatile 禁止指令重排序,保證「初始化完成」對其他線程可見,成就高性能線程安全懶漢。
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
}
優點:天然線程安全(ClassLoader 機制)、代碼最簡。
缺點:無法延遲加載,若實例重且不常用浪費內存;無法依賴外部參數構造。
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}
利用類加載機制:外部類加載時不加載內部類,首次調用 getInstance() 觸發 Holder 初始化,兼具延遲加載 + 線程安全 + 代碼簡潔。
public enum EasySingleton { INSTANCE; }
JVM 保證枚舉實例唯一性,天然抵禦:
readResolve() 防禦;枚舉無此憂;Constructor.setAccessible(true) 可暴力調用私有構造器,枚舉類型 JVM 直接阻攔。| 手段 | 原理 | 防禦 |
|---|---|---|
| 序列化/反序列化 | 反序列化繞過構造器創建新對象 | 實現 readResolve() 返回現有實例;或用枚舉 |
| 反射 | Constructor.setAccessible(true) 調用私有構造器 | 構造器內判斷 if (INSTANCE != null) throw Exception;或用枚舉 |