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

Java 並發核心:synchronized 與 volatile 深度對比與實戰選型

     編輯:Java 綜合教學

核心問題:可見性 vs 原子性

多線程下對共享變量的訪問,核心衝突在於可見性(一線程寫入,另一線程能否立即看到)與原子性(操作不可分割)。volatile 解決可見性,synchronized 同時保證可見性與原子性(互斥)。

JMM 與 Happens-Before 模型

Java 記憶體模型(JMM)規定:若操作 A 的結果需對操作 B 可見,則 A happens-before B。此關係跨線程也成立。

volatile 內存語義

  • 寫:立即刷新工作記憶體至主記憶體。
  • 讀:直接從主記憶體讀取,忽略工作記憶體副本。
  • 編譯器與 CPU 禁止指令重排(內存屏障)。

synchronized 內存語義

  • 進入同步塊:讀取主記憶體最新值到工作記憶體(可見性)。
  • 退出同步塊:將工作記憶體修改刷回主記憶體(可見性)。
  • 全程持有鎖,保證互斥,從而實現原子性。

實戰驗證:可見性與原子性邊界

案例 1:volatile 修復可見性失效

// 無 volatile:主線程修改 flag,子線程 while(true) 可能永遠不見更新
private volatile boolean isRunning = true; // 加上 volatile 後,子線程每次讀取強制從主存獲取,循環終止

案例 2:volatile 無法保證原子性

static volatile int count = 0;
// 100 線程各自 count++ 100 次,理論 10000,實際遠小於
// count++ 非原子:read-modify-write 三步,多線程交織導致丟失更新

案例 3:synchronized 保證原子性

private synchronized static void addCount() { // 鎖住方法
    for (int i = 0; i < 100; i++) count++;
}
// 輸出穩定遞增至 10000

選型決策表

場景推薦方案理由
單一狀態標誌(開關、標記)volatile輕量,無鎖開銷
複合操作(檢查-執行、累加、多變量不變式)synchronized / Lock需要原子性與互斥
高併發讀多寫少ReentrantReadWriteLock / StampedLock讀共享寫互斥,吞吐更高
簡單計數器AtomicLong / LongAdder無鎖 CAS,性能優於 synchronized

進階提示

  • synchronized 鎖升級(偏向鎖 → 輕量級鎖 → 重量級鎖)在 JDK 1.6+ 高度優化,非極端爭用下性能極佳。
  • volatile 適用於「單次寫入、多次讀取」的發布模式(如 DCL 單例的 instance 字段)。
  • 避免過度同步:縮小鎖範圍、使用線程本地變量、不可變物件。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved