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

Java單例模式全景圖:從懶漢、餓漢到雙重檢查鎖、靜態內部類、枚舉,含破壞與防禦

     編輯:Java 綜合教學

Java單例模式全景圖:從懶漢、餓漢到雙重檢查鎖、靜態內部類、枚舉,含破壞與防禦

1. 懶漢式——延遲加載

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) instance = new Singleton();
        return instance;
    }
}

優點:用時才創建。缺點:線程不安全,多線程並發會創建多個實例。

2. 同步鎖版懶漢

public static synchronized Singleton getInstance() { ... }

線程安全,但鎖粒度過大,實例化後每次調用仍同步,性能差。

3. 雙重檢查鎖(DCL)—— 看似完美,實有陷阱

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

問題:instance = new Singleton() 非原子,JVM 指令重排序可能導致:
1. 分配內存 → 2. 初始化對象 → 3. 引用指向內存
重排為 1→3→2 時,其他線程見 instance != null 卻拿到半初始化對象。

4. volatile 修復 DCL

private static volatile Singleton instance;

volatile 禁止指令重排序,保證「初始化完成」對其他線程可見,成就高性能線程安全懶漢。

5. 餓漢式——類加載即初始化

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}

優點:天然線程安全(ClassLoader 機制)、代碼最簡。
缺點:無法延遲加載,若實例重且不常用浪費內存;無法依賴外部參數構造。

6. 靜態內部類——推薦寫法

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

利用類加載機制:外部類加載時不加載內部類,首次調用 getInstance() 觸發 Holder 初始化,兼具延遲加載 + 線程安全 + 代碼簡潔。

7. 枚舉單例——大師推薦,防序列化/反射破壞

public enum EasySingleton { INSTANCE; }

JVM 保證枚舉實例唯一性,天然抵禦:

  • 序列化/反序列化:普通單例反序列化會產生新對象,需加 readResolve() 防禦;枚舉無此憂;
  • 反射攻擊:Constructor.setAccessible(true) 可暴力調用私有構造器,枚舉類型 JVM 直接阻攔。

破壞單例的兩大手段 & 防禦

手段原理防禦
序列化/反序列化反序列化繞過構造器創建新對象實現 readResolve() 返回現有實例;或用枚舉
反射Constructor.setAccessible(true) 調用私有構造器構造器內判斷 if (INSTANCE != null) throw Exception;或用枚舉

總結建議

  • 一般場景:靜態內部類或枚舉;
  • 需延遲加載且依賴外部參數:volatile + DCL;
  • 極簡場景、確定會用:餓漢式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved