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

Java 線程同步核心:synchronized 與 volatile 深度解析與選型指南

     編輯:Java 相關知識

synchronized 與 volatile 是 Java 併發編程的雙基石。本文從內存模型(JMM)視角剖析兩者的語義差異、保證範圍、性能特徵,給出場景化選型建議。

一、 線程不安全根源:可見性與有序性

  • 可見性:線程 A 修改共享變量,線程 B 能否立即看到?JMM 規定:線程工作內存 → 主內存 刷新無時序保證。
  • 有序性:編譯器/CPU 為優化會重排序指令。單線程語義不變,多線程下可導致邏輯錯誤(如雙重檢查鎖失效)。

二、 synchronized:重量級互斥鎖(JDK 6+ 鎖優化後輕量化)

兩種用法

// 1. 同步代碼塊:鎖對象顯式指定,粒度可控
public void output(String name) {
    synchronized (this) { // 或共享 final Object lock = new Object();
        for (char c : name.toCharArray()) System.out.print(c);
    }
}

// 2. 同步方法:隱式鎖 this / Class對象
public synchronized void output(String name) { ... }
public static synchronized void staticMethod() { ... } // 鎖 Class對象

執行流程(JMM 語義)

  1. 獲取監視器鎖
  2. 清空工作內存
  3. 從主內存拷貝共享變量副本
  4. 執行代碼
  5. 刷新主內存
  6. 釋放鎖

語義保證:互斥性 + 可見性 + 有序性。同步塊內操作對其他線程原子可見,且禁止指令重排序跨越同步塊邊界。

三、 volatile:輕量級內存屏障

語義保證

  • 可見性:寫入 volatile 變量立即刷新主內存;讀取強制從主內存加載。
  • 有序性(部分):禁止指令重排序跨越 volatile 讀寫(JMM happens-before 規則)。
  • 不保證原子性:volatile int i; i++; 非原子(讀-改-寫三步)。

經典失效示例

class Test {
    static volatile int i = 0, j = 0;
    static void writer() { i++; j++; }      // 兩步非原子
    static void reader() { System.out.println("i=" + i + " j=" + j); }
}
// 可能輸出 j > i:writer 執行 i++ 後、j++ 前,reader 讀到舊 i、新 j

volatile 只能保證單個變量讀寫的可見/有序,無法協調多變量不變量。

四、 核心對比表

維度synchronizedvolatile
互斥性✅ 完整互斥❌ 無
可見性✅ 鎖釋放前刷新主內存✅ 讀寫直接主內存
有序性✅ 禁止重排跨越同步塊✅ 禁止重排跨越讀寫
原子性✅ 同步塊整體原子❌ 單讀/單寫原子,復合操作非原子
阻塞性可能阻塞(鎖競爭)不阻塞
適用對象代碼塊/方法/任意對象單個字段(基本類型/引用)
性能特徵輕量鎖/偏向鎖/重量鎖升級,競爭激烈時開銷大極低,接近普通讀寫,寫入有內存屏障開銷

五、 經典適用場景

✅ 用 synchronized

  • 復合操作原子性:i++、check-then-act、集合非原子操作
  • 多變量不變量:轉賬(余額 -10 +10 必須原子)
  • 臨界區邏輯複雜、需互斥

✅ 用 volatile

  • 狀態標誌位:volatile boolean running = true; 線程退出信號
  • 單次寫入、多次讀取:配置對象引用 volatile Config config;(構建完成後發布)
  • 雙重檢查鎖 (DCL) 中的實例引用:volatile Singleton instance;
  • 獨立觀察變量:統計計數器(配合 AtomicLong 更好)

六、 進階:JDK 5+ 顯式鎖 Lock 與 Atomic* 補完

  • ReentrantLock:可中斷、超時、公平鎖、多條件 Condition
  • AtomicInteger/Long/Reference:CAS 無鎖原子操作,計數器首選
  • LongAdder:高併發計數器,分段累加,吞吐碾壓 AtomicLong

七、 避坑口訣

  1. volatile 不能替代 synchronized 處理復合操作。
  2. synchronized 鎖對象必須是共享、不可變、final 引用。
  3. volatile + final 互斥(final 已保證發布安全,volatile 多餘)。
  4. 高併發熱點優先 Atomic*/LongAdder / ThreadLocal 避免共享。

總結:synchronized 是「大錘」,保證全套語義;volatile 是「手術刀」,精準解決單變量發布可見與有序。 理解 JMM happens-before 規則,按「能用 volatile 不用 synchronized,能用 Atomic 不用鎖,能用無共享不共享」原則選型,是併發編程的成熟標誌。

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