單例模式是面試必考、生產常用的創建型模式。本文系統分析五種經典實現的優缺點、適用場景與 JDK 版本依賴。
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;
}
缺點:鎖粒度過大,實例創建後每次調用仍同步,性能差。
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
}
優點:JVM 保證類初始化線程安全,無同步開銷。
缺點:無法實現懶加載;若構造器依賴外部配置/參數則不適用。
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();
}
}
return instance;
}
}
關鍵點:
① volatile 禁止指令重排序,確保 new Singleton() (分配內存→初始化→賦值引用) 可見性。
② JDK 1.5+ 修復了 volatile 語義,之前版本不可靠。
③ 現代 JVM 對未鎖定讀取優化極好,性能接近餓漢式。
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}
原理:
① 外部類載入不初始化內部類。
① 調用 getInstance() 觸發 Holder 類初始化,JVM 保證 <clinit> 線程安全 (JLS §12.4.2)。
③ 兼具懶加載與零同步開銷,推薦生產環境使用。
public enum Singleton { INSTANCE; }
優勢:
① 天然線程安全、防反射攻擊 (構造器私有)、防序列化重複實例化。
② 代碼最簡潔,語義明確。
③ 缺點:無法懶加載、無法繼承、無法實現帶參構造。
| 寫法 | 懶加載 | 線程安全 | 性能 | 防反射/序列化 | 推薦場景 |
|---|---|---|---|---|---|
| 懶漢(無鎖) | 是 | 否 | 高 | 否 | 單線程/教學 |
| 懶漢(synchronized) | 是 | 是 | 低 | 否 | 低併發/舊JDK |
| 餓漢 | 否 | 是 | 高 | 否 | 啟動即需實例、配置簡單 |
| DCL+volatile | 是 | 是(JDK5+) | 高 | 否 | 高併發、需懶加載 |
| 靜態內部類 | 是 | 是 | 高 | 否 | 通用首選 |
| 枚舉 | 否 | 是 | 高 | 是 | 安全要求極高、無懶加載需求 |
static final 初始化順序:靜態字段按聲明順序 → 靜態代碼塊按順序,均在類初始化階段 (<clinit>) 執行。Supplier 延遲初始化或 Builder 模式。Constructor.setAccessible(true) 可調用私有構造器,枚舉類無此漏洞。readResolve() 返回唯一實例可修復。