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

Java 單例模式四大寫法深度對比:餓漢、靜態內部類、雙重檢查鎖、枚舉——含線程安全、序列化、反射攻擊防禦

     編輯:Java 程式設計入門

寫法一:餓漢式(類加載即初始化)

public class SingletonEager {
    private static final SingletonEager INSTANCE = new SingletonEager();
    private SingletonEager() {}
    public static SingletonEager getInstance() { return INSTANCE; }
}
  • ✅ 線程安全(ClassLoader 鎖)
  • ✅ 實現最簡單
  • ❌ 無延遲加載,啟動即佔用資源
  • ❌ 構造拋異常導致類不可用
  • ❌ 可被反射破壞:Constructor.setAccessible(true) 可創建第二個實例
  • ❌ 可被序列化破壞:需實現 readResolve() 返回 INSTANCE

寫法二:靜態內部類(延遲加載 + 線程安全 + 簡潔)⭐ 推薦通用首選

public class SingletonHolder {
    private SingletonHolder() {}
    private static class Holder {
        static final SingletonHolder INSTANCE = new SingletonHolder();
    }
    public static SingletonHolder getInstance() { return Holder.INSTANCE; }
}
  • ✅ 延遲加載:調用 getInstance() 觸發 Holder 類初始化
  • ✅ 線程安全:JVM 保證類初始化原子性
  • ✅ 無鎖、無 volatile、代碼極簡
  • ❌ 同樣可被反射/序列化破壞(需額外防禦)

寫法三:雙重檢查鎖(DCL,JDK 5+ 必須加 volatile)

public class SingletonDCL {
    private static volatile SingletonDCL instance; // 關鍵:禁止指令重排序
    private SingletonDCL() {}
    public static SingletonDCL getInstance() {
        if (instance == null) {                    // 第一次檢查:無鎖快速路徑
            synchronized (SingletonDCL.class) {    // 類級鎖
                if (instance == null) {            // 第二次檢查:防重複創建
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}
  • ✅ 延遲加載、高併發性能好(僅首次同步)
  • ⚠️ volatile 必不可少:防止 instance = new Singleton() 重排序導致半初始化對象可見
  • ❌ 代碼較繁瑣,易寫錯(漏 volatile、漏二次檢查、鎖對象錯誤)
  • ❌ 同樣怕反射/序列化

寫法四:枚舉單例(Effective Java 作者 Josh Bloch 強推)🛡️ 最強防禦

public enum SingletonEnum {
    INSTANCE;
    // 可添加字段、方法
    public void doSomething() { System.out.println("價值連城"); }
}
  • ✅ 天然線程安全(枚舉類初始化由 ClassLoader 保證)
  • ✅ 天然防反射:JVM 禁止通過反射調用枚舉構造器
  • ✅ 天然防序列化破壞:序列化機制特殊處理,反序列化直接返回枚舉常量
  • ✅ 代碼最少,語義最清晰
  • ❌ 無延遲加載(枚舉類加載時即創建)
  • ❌ 不支持繼承、不支持懶加載邏輯(如配置讀取後再初始化)

防反射/序列化通用加固模板(適用前三種)

public class HardenedSingleton implements Serializable {
    private static final long serialVersionUID = 1L;
    private static volatile HardenedSingleton instance;

    private HardenedSingleton() {
        // 防反射:若已存在實例則拋異常
        if (instance != null) {
            throw new IllegalStateException("單例已存在,禁止反射創建");
        }
    }

    public static HardenedSingleton getInstance() {
        if (instance == null) {
            synchronized (HardenedSingleton.class) {
                if (instance == null) instance = new HardenedSingleton();
            }
        }
        return instance;
    }

    // 防序列化:反序列化時返回現有實例
    protected Object readResolve() { return instance; }
}

對比總結表

維度餓漢式靜態內部類DCL枚舉
線程安全✅✅✅ (volatile)✅
延遲加載❌✅✅❌
性能極高極高高極高
代碼複雜度低低中極低
防反射❌❌❌✅
防序列化❌❌❌✅
推薦場景輕量、必用、啟動即需通用首選需精確控制初始化時機安全要求極高、框架級組件

實戰選型建議

  1. 一般業務單例:靜態內部類,平衡延遲加載、線程安全、代碼簡潔。
  2. 框架核心組件、安全敏感組件:枚舉單例,零成本防反射/序列化破壞。
  3. 需動態參數初始化(如讀取配置後才能 new):DCL + volatile 或 AtomicReference + CAS。
  4. Spring 管理的 Bean:直接用 @Component @Scope("singleton"),容器保證單例、生命周期、AOP 代理一致性,不要手寫單例。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved