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

Java 單例模式 volatile 可見性深度解析:為何 synchronized 已足夠

     編輯:程式設計綜合問答

程式碼

public class Singleton {
    private volatile static Singleton instance = null;

    private Singleton() {}

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

問題

instance 加 volatile 是否必要?若不加,instance = new Singleton() 後,其他執行緒是否可能暫時看不到?

結論:此雙重檢查鎖定 (DCL) 模式下,**volatile 非必要**,synchronized 已保證可見性與有序性。

詳細分析

  1. synchronized 語義 (JMM):
    • **解鎖 (unlock) 前** 的所有寫入,**對後續獲得同一鎖 (lock) 的執行緒可見**。
    • 編譯器/處理器**不會將同步區塊外的指令移入同步區塊內**,也**不會將同步區塊內的指令移出**。
  2. DCL 執行流程:
    1. 執行緒 A 進入 synchronized,檢查 instance == null,建立物件,賦值給 instance。
    2. 執行緒 A 退出 synchronized (unlock) → **發生 happens-before 邊緣**。
    3. 執行緒 B 進入 synchronized (lock) → **保證看到 A 所有寫入**,包括 instance 參考與物件內部初始化完成狀態。
  3. volatile 的作用:保證**讀取時直接從主記憶體獲取最新值**,禁止指令重排 (寫入屏障)。但在 DCL 中,**鎖已提供相同保證**,volatile 只會增加額外的記憶體屏障開銷 (微乎其微)。

何時 volatile 必要?

  • **無鎖單寫多讀**場景:如 volatile boolean flag 控制執行緒停止。
  • **長整數/雙精度原子性** (32 位元 JVM):volatile long/double 保證 64 位元讀寫原子。
  • **發布不可變物件** (非 DCL):volatile ImmutableObj ref 確保其他執行緒看到完全初始化的物件。

更簡潔寫法:靜態內部類 (推薦)

public class Singleton {
    private Singleton() {}

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

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}
  • 利用**類別載入機制**天然執行緒安全、延遲初始化、無鎖高效能。
  • JVM 保證 Holder 類別初始化時的 INSTANCE 建構過程對所有執行緒可見。

總結

DCL + synchronized **不需要 volatile**;現代 Java 開發**首選靜態內部類**實現單例,簡潔、安全、高效。

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