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

Java 單例模式五種寫法深度解析:從懶漢到雙重檢查鎖的演進之路

     編輯:Java 程式設計入門

單例模式確保類在全局僅有一個實例,並提供全局訪問點。本文梳理五種經典實現,從線程不安全到高性能線程安全,層層遞進。

1. 懶漢式(線程不安全)


public class Singleton1 {
    private static Singleton1 instance;
    private Singleton1() {}
    public static Singleton1 getInstance() {
        if (instance == null) {
            instance = new Singleton1();  // 多線程下可能創建多個實例
        }
        return instance;
    }
}

❌ 多線程併發時會創建多個實例。

2. 懶漢式 + synchronized(線程安全但性能低)


public class Singleton2 {
    private static Singleton2 instance;
    private Singleton2() {}
    public static synchronized Singleton2 getInstance() {  // 鎖住整個方法
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}

✅ 線程安全,❌ 每次調用都同步,性能差,實際只需首次創建時同步。

3. 餓漢式(類加載時創建,天然線程安全)


public class Singleton3 {
    private static final Singleton3 instance = new Singleton3();
    private Singleton3() {}
    public static Singleton3 getInstance() { return instance; }
}

✅ 簡單、線程安全(類加載機制保證),❌ 無懶加載,未使用也會創建,浪費資源。

4. 靜態內部類(懶加載 + 線程安全 + 高性能)


public class Singleton4 {
    private Singleton4() {}
    public static Singleton4 getInstance() {
        return Holder.INSTANCE;
    }
    private static class Holder {
        private static final Singleton4 INSTANCE = new Singleton4();
    }
}

✅ 推薦寫法:

  • 外部類加載時不加載內部類,實現懶加載
  • 內部類首次被引用時初始化,JVM 保證線程安全
  • 無同步開銷,性能極高

5. 雙重檢查鎖 DCL(懶加載 + 線程安全 + 低同步開銷)


public class Singleton5 {
    private volatile static Singleton5 instance;  // 關鍵:volatile 防指令重排
    private Singleton5() {}
    public static Singleton5 getInstance() {
        if (instance == null) {                    // 第一次檢查,無鎖
            synchronized (Singleton5.class) {      // 同步塊
                if (instance == null) {            // 第二次檢查
                    instance = new Singleton5();   // 創建實例
                }
            }
        }
        return instance;
    }
}

為什麼需要 volatile?

instance = new Singleton5() 非原子操作,實際分三步:

  1. 分配內存
  2. 初始化對象
  3. 將引用指向內存地址

JVM 可能為優化進行指令重排(1→3→2),導致其他線程看到 instance != null 但對象未初始化完成。volatile 禁止重排,保證可見性與有序性。

對比總結

寫法懶加載線程安全性能推薦度
懶漢式✅❌高❌
同步懶漢✅✅低❌
餓漢式❌✅高⚠️ 簡單場景
靜態內部類✅✅高✅ 首選
DCL✅✅高✅ 兼容舊 JDK

現代最佳實踐:枚舉單例


public enum Singleton {
    INSTANCE;
    public void doSomething() { ... }
}

✅ Effective Java 首選:

  • 天然線程安全(枚舉類加載機制)
  • 天然防反射破解、防序列化重新創建
  • 代碼最簡潔,語義最清晰

破壞單例的手段與防禦

總結

現代 Java 開發中,枚舉單例為首選,靜態內部類為次選(需懶加載且不想用枚舉),DCL 僅用於舊版本兼容。餓漢式適用於資源輕量、必定使用的場景。懶漢式與同步懶漢式不推薦生產使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
手段防禦
反射調用私有構造器構造器中檢查 if (instance != null) throw ...,或用枚舉
反序列化實現 readResolve() 返回現有實例,或用枚舉
克隆不實現 Cloneable,或拋異常