前言
線程安全本質是控制多線程對共享資源的**有序訪問**與**可見性**。本文從 JVM 記憶體模型 (JMM) 出發,系統梳理 synchronized、volatile、wait/notify 核心機制,並以經典生產者-消費者模式為例展示實戰應用。
一、JVM 記憶體模型 (JMM) 基石
主記憶體與工作記憶體
- 主記憶體:所有線程共享,存放實例對象、靜態欄位、數組元素。
- 工作記憶體:每線程私有,存放主存變數副本 (對應 CPU 寄存器 + L1/L2 快取)。
八大原子操作
read (主存→工作存) load (載入工作存)
use (工作存→引擎運算) assign (運算結果→工作存)
store (工作存→主存) write (寫入主存)
lock (主存加鎖) unlock (主存解鎖)
JMM 規範這些操作的順序與可見性,屏蔽底層硬體/OS 差異。
核心問題:可見性與有序性
- 可見性:線程 A 修改共享變數,線程 B 能否立即看到?
→ 工作記憶體隔離導致「髒讀」。
- 有序性:程序順序 vs 執行順序。
→ 編譯器優化、CPU 指令重排、快取回寫延遲導致亂序。
二、synchronized:重量級互斥鎖 (JDK 1.6+ 鎖升級優化)
語法形式
volatile int i = 0;
i++; // 讀-改-寫三步,非原子!多線程下會丟失更新
適用場景 (雙重檢查)
- 寫入不依賴當前值 (單純賦值、狀態標記)。
- 不參與不變式約束 (無多變量協同)。
class Plate {
private final List
五、進階:阻塞隊列 BlockingQueue 取代手工 wait/notify
| 機制 | 原子性 | 可見性 | 有序性 | 適用場景 |
| synchronized | ✅ | ✅ | ✅ | 複雜臨界區、複合操作、需互斥 |
| volatile | ❌ | ✅ | ✅ | 狀態旗標、單次發布、獨立觀察 |
| Atomic* / LongAdder | ✅ (CAS) | ✅ | ✅ | 計數器、累加器、單變量原子更新 |
| ReentrantLock | ✅ | ✅ | ✅ | 需可中斷、超時、公平、多條件 |
| BlockingQueue | ✅ | ✅ | ✅ | 生產消費、任務調度、線程池工作隊列 |
最佳實務
- 優先使用 **線程安全集合 / BlockingQueue / Atomic 類**,減少手工同步。
- 必須手工同步時,**鎖粒度最小化**、**持鎖時間最短化**、**避免持鎖調用外部方法**。
volatile 僅用於**單寫單讀/多讀單寫**獨立變量,複雜邏輯用 synchronized。
- 掌握 **JMM happens-before 規則**:程序順序、鎖規則、volatile 規則、線程啟動/終止、傳遞性。