多線程下對共享變量的訪問,核心衝突在於可見性(一線程寫入,另一線程能否立即看到)與原子性(操作不可分割)。volatile 解決可見性,synchronized 同時保證可見性與原子性(互斥)。
Java 記憶體模型(JMM)規定:若操作 A 的結果需對操作 B 可見,則 A happens-before B。此關係跨線程也成立。
// 無 volatile:主線程修改 flag,子線程 while(true) 可能永遠不見更新
private volatile boolean isRunning = true; // 加上 volatile 後,子線程每次讀取強制從主存獲取,循環終止
static volatile int count = 0;
// 100 線程各自 count++ 100 次,理論 10000,實際遠小於
// count++ 非原子:read-modify-write 三步,多線程交織導致丟失更新
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 字段)。