單例模式屬於創建型模式,確保某個類在整個應用程式生命週期中只有一個實例,並提供全域存取點。常見場景:資料庫連線池、日誌物件、配置管理器等資源密集型物件。
new 實例。getInstance())回傳實例。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;
}
每次呼叫都加鎖,99% 情況下無需同步,效能差。
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) instance = new Singleton();
}
}
return instance;
}
volatile 禁止指令重排,確保其他線程見到完全初始化的物件。JDK 5 前 JMM 有缺陷,不可用。
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
利用 ClassLoader 機制天然線程安全,無鎖高效。缺點:無法延遲載入,若建構子依賴外部配置則不適用。
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() { return Holder.INSTANCE; }
外部類載入時不初始化 Holder,只有呼叫 getInstance() 才會觸發初始化,兼具 lazy loading 與高效能,不依賴 JDK 版本。
public enum Singleton { INSTANCE; public void doSomething() {} }
Josh Bloch 大力推薦。序列化機制天然保證單一實例,反射無法破壞,程式碼最簡潔。
| 場景 | 推薦方式 | 理由 |
|---|---|---|
| 一般業務,無特殊需求 | 靜態內部類 | 簡潔、延遲載入、高效能 |
| 需防序列化/反射破壞 | 列舉 | 語言層面保證,最安全 |
| 建構子依賴外部參數/配置 | DCL 或靜態內部類+建構子參數 | 支援延遲初始化並傳參 |
| 極簡原型/測試代碼 | 餓漢式 | 一行搞定,無負擔 |
synchronized 加在方法上 vs 代碼塊:代碼塊粒度更細,效能更好。volatile 導致 DCL 失效(指令重排)。readResolve() 或改用列舉。單例模式核心在於私有建構子 + 靜態工廠方法。實務上靜態內部類與列舉足以覆蓋 99% 場景,建議作為預設選擇;僅在極特殊需求下考慮 DCL。