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

Java 單例模式實戰:線程不安全到雙重檢查鎖 (DCL) 完整演進

     編輯:Java 綜合教學

版本 1:懶漢式 (線程不安全)


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

缺陷:多線程並發進入 if 導致多次實例化。

驗證並發問題 (CountDownLatch 模擬)


final int CONCURRENT = 10;
ExecutorService pool = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(CONCURRENT);
for (int i = 0; i < CONCURRENT; i++) {
    pool.execute(() -> {
        latch.countDown();
        try { latch.await(); } catch (InterruptedException ignored) {}
        SingletonV1.getInstance(); // 並發調用
    });
}
latch.await(); pool.shutdown();
// 控制台可能打印多次 "實例化" 日誌

版本 2:同步方法 (性能差)


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

每次調用加鎖,高並發下嚴重爭用。

版本 3:雙重檢查鎖 (DCL) - 推薦


public class SingletonDCL {
    // volatile 禁止指令重排,保證其他線程看到完全初始化的對象
    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 SingletonDCL() 的指令重排 (分配內存 → 初始化對象 → 賦值引用),避免其他線程看到「半初始化」對象。

版本 4:靜態內部類 (JVM 保證線程安全、延遲加載)


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

原理:類加載機制保證 Holder 類首次被主動使用時才初始化,且初始化過程由 JVM 鎖同步,天然線程安全、無鎖開銷、代碼簡潔。

版本 5:枚舉單例 (Effective Java 推薦)


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

優勢:防反射破解、防反序列化重新創建、語法最簡潔、JVM 保證單例。

總結選型建議

場景推薦版本
一般業務、需延遲加載靜態內部類 (版本 4)
需防反射/序列化攻擊枚舉 (版本 5)
需手動控制實例化時機/參數DCL (版本 3)
簡單工具類、無狀態餓漢式 (類加載時創建)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved