併發編程難點在於正確處理共享可變數據。本文從五大理論支柱構建知識體系,配合代碼驗證與 JMM 圖解。
若數據僅線程內可見,不存在安全問題。多線程必然共享數據(如數據庫連接池、緩存、單例)。
// 10線程各加100次,預期1000,實際<1000
static int count = 0;
void addCount() { count++; } // 非原子,競態條件
synchronized 提供獨佔鎖,分共享鎖(讀)與排它鎖(寫)。addCount() 串行化,結果恆為 1000。i++ 三步:讀取、加一、寫回。多線程交織導致更新丟失。
// 線程A讀取100,線程B讀取100,均寫回101,丟失一次更新
AtomicStampedReference 解決。
volatile/synchronized 強制刷新/失效緩存,保證可見性。// 可見性失效示例 static boolean ready = false; static int number = 0; // Writer: number=100; ready=true; // Reader: if(!ready) print ready; print number; // 可能印 false 0
編譯器、處理器、內存系統三類重排序:
// JSR-133 合法重排示例 // 初始 x=y=0 // 線程1: x=1; r1=y; // 線程2: y=1; r2=x; // 可能結果 r1=0, r2=0 (指令重排導致)
volatile/synchronized 建立 happens-before 關系,禁止特定重排。掌握共享性→互斥性→原子性→可見性→有序性五層遞進關系,是寫出正確高效併發代碼的理論基石。後續實戰工具(JUC 包、並發集合、同步器)均建立於此。