首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 四大單例模式深度解析:從餓漢到靜態內部類的演進之路

     編輯:Java 相關知識

單例模式核心三要素

  1. 構造器私有化(防外部實例化、防繼承)
  2. 靜態私有實例(類級唯一)
  3. 公開靜態獲取方法(全局訪問點)

1. 餓漢式:類加載即創建,最簡單線程安全

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}
  • ✅ 線程安全(類加載機制保證)
  • ✅ 實現簡單
  • ❌ 無延遲加載,若實例構建耗時/資源佔用大且未必用到,造成浪費

2. 懶漢式(同步方法):用到時創建,但效能低

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (instance == null) instance = new Singleton();
        return instance;
    }
}
  • ✅ 延遲加載
  • ✅ 線程安全
  • ❌ 每次調用加鎖,併發效能極差

3. 雙重檢查鎖(DCL):延遲 + 高效能(JDK 5+ 必須 volatile)

public class Singleton {
    private volatile static Singleton instance; // 關鍵:禁止指令重排
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) { // 第一次檢查,無鎖讀取
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次檢查,加鎖創建
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

為什麼需要 volatile?

instance = new Singleton() 非原子操作:① 分配內存 ② 初始化對象 ③ 引用指向內存。無 volatile 時,JVM 可能重排為 ①→③→②,導致線程 B 看到 instance != null 但對象未初始化完成,直接返回半成品對象。volatile 禁止重排,保證 happens-before 可見性。

4. 靜態內部類:延遲 + 線程安全 + 代碼簡潔(推薦)

public class Singleton {
    private Singleton() {}
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() { return Holder.INSTANCE; }
}
  • ✅ 外部類加載時不初始化 Holder,真正調用 getInstance() 才觸發內部類加載 → 延遲加載
  • ✅ 類加載機制天然線程安全,無需同步鎖
  • ✅ 代碼簡潔,無 volatile 複雜性
  • ✅ 防反序列化破壞(需額外實現 readResolve())

對比總結表

模式延遲加載線程安全效能複雜度推薦指數
餓漢式❌✅高低⭐⭐⭐
懶漢式(同步)✅✅低低⭐
DCL✅✅高中⭐⭐⭐⭐
靜態內部類✅✅高低⭐⭐⭐⭐⭐

進階:防反射/反序列化破壞單例

// 構造器中添加防禦
private Singleton() {
    if (Holder.INSTANCE != null) throw new IllegalStateException("已存在實例");
}

// 實現 readResolve 防反序列化創建新對象
private Object readResolve() { return Holder.INSTANCE; }

總結

日常開發首選靜態內部類,兼顧延遲加載、線程安全、高效能、代碼簡潔。若極度追求啟動速度且實例輕量,餓漢式也可接受。DCL 僅在需精確控制初始化時機(如依賴外部配置)時考慮。切記:雙重檢查鎖少寫 volatile 是生產事故高發區。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved