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

Java 多線程單例模式最佳實踐:雙重檢查鎖、volatile、靜態內部類、枚舉

     編輯:Java 相關知識

單例核心要求

  1. 構造器私有化
  2. 提供全局靜態訪問點 getInstance()
  3. 確保多線程環境下只創建一個實例
  4. 性能優化:避免每次調用都同步

方案對比

方案線程安全延遲加載性能推薦度
懶漢式 + synchronized 方法✅✅❌ 每次調用加鎖⭐
雙重檢查鎖 (DCL) + volatile✅✅✅ 首次加鎖,後續無鎖⭐⭐⭐⭐
靜態內部類✅✅✅ 類加載機制天然保證⭐⭐⭐⭐⭐
枚舉✅❌ (類加載時創建)✅ 極簡⭐⭐⭐⭐⭐
餓漢式✅❌✅⭐⭐⭐

推薦一:靜態內部類(最優雅)

public final class Singleton {
    private Singleton() {}

    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}
  • 利用 JVM 類加載機制:Holder 類只在首次調用 getInstance() 時被加載、初始化
  • 無鎖、無 volatile、代碼極簡、防序列化破壞(需加 readResolve)

推薦二:枚舉(最防禦)

public enum Singleton {
    INSTANCE;
    // 可添加字段、方法
    public void doSomething() { ... }
}
  • JVM 保證枚舉常量唯一性
  • 天然防反射攻擊(構造器私有化不可繼承)
  • 天然防序列化破壞
  • 缺點:無延遲加載,無法繼承

推薦三:雙重檢查鎖 (DCL) —— 面試必考

public final class Singleton {
    private volatile static Singleton instance; // 關鍵:volatile 禁止指令重排

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {                 // 第一次檢查(無鎖)
            synchronized (Singleton.class) {    // 類級別鎖
                if (instance == null) {         // 第二次檢查(有鎖)
                    instance = new Singleton(); // 實例化
                }
            }
        }
        return instance;
    }
}

為什麼需要 volatile?

instance = new Singleton() 非原子操作:

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

JVM 可能重排為 1→3→2,導致另一線程看到 instance != null 但對象未初始化完成。volatile 禁止重排,保證可見性與有序性。

Lock 版本(教學用,不推薦生產)

private static final Lock lock = new ReentrantLock();
private static Singleton instance;

public static Singleton getInstance() {
    if (instance == null) {
        lock.lock();
        try {
            if (instance == null) instance = new Singleton();
        } finally {
            lock.unlock();
        }
    }
    return instance;
}

語義清晰,但比 synchronized 繁瑣,且無 JVM 優化(偏向鎖、輕量級鎖)。

synchronized 方法版本(有 Bug)

// ❌ 錯誤示範:每次調用返回新實例!
public static synchronized Singleton getInstance() {
    if (instance == null) instance = new Singleton();
    return new Singleton(); // Bug: 應返回 instance
}

防反射與序列化破壞

// 靜態內部類 / DCL 補強
private Singleton() {
    if (Holder.INSTANCE != null) // 或 instance != null
        throw new IllegalStateException("單例已存在");
}

// 防序列化
protected Object readResolve() {
    return getInstance(); // 反序列化時返回唯一實例
}

枚舉天然免疫上述攻擊。

總結選型建議

  1. 一般業務:靜態內部類,簡潔、延遲、安全、性能四全
  2. 安全敏感/框架底層:枚舉,極致防禦
  3. 面試手寫:DCL + volatile,展示對 JMM 理解
  4. Spring Bean:直接用 @Scope("singleton"),容器管理

切記:單例不是萬能的,高併發下可能成為瓶頸,考慮無狀態設計或 ThreadLocal/對象池替代。

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