volatile 是 Java 並發編程中「輕量級的同步機制」,僅修飾變量,不提供原子性,但保證可見性與有序性,禁止指令重排序。本文從底層原理、兩大核心語義、經典應用場景三個維度深度剖析。
| 語義 | 說明 | 底層實現 |
|---|---|---|
| 可見性 | 一線程寫入,其他線程立即可見最新值 | 寫入時刷新至主存;讀取時失效本地緩存,強制從主存讀取(緩存一致性協議 MESI) |
| 有序性 | 禁止 volatile 變量前後的指令重排序 | 編譯器屏障 + CPU 內存屏障: 寫屏障:StoreStore + StoreLoad 讀屏障:LoadLoad + LoadStore |
// 線程 1
boolean stop = false;
while (!stop) { doSomething(); }
// 線程 2
stop = true; // 可能永遠不被線程 1 看到!
原因:線程 1 將 stop 複製到工作內存(CPU 緩存),線程 2 修改主存值未及時刷新/通知,線程 1 繼續讀取舊值。
volatile boolean stop = false; // 線程 2 寫入時:強制刷新主存 + 發送總線信號使其他 CPU 緩存行失效 // 線程 1 讀取時:發現緩存行無效,從主存重新加載最新值
問題背景:instance = new Singleton() 非原子操作,分三步:
JVM 允許 2、3 重排序,導致其他線程看到「非 null 但未初始化完成」的對象。
public class Singleton {
private volatile static Singleton instance; // 關鍵:禁止重排序
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次檢查(無鎖,高性能)
synchronized (Singleton.class) { // 加鎖
if (instance == null) { // 第二次檢查
instance = new Singleton(); // 初始化(volatile 保證有序)
}
}
}
return instance;
}
}
原理:volatile 寫屏障確保「初始化完成」先於「引用賦值」對其他線程可見,讀屏障確保獲取引用後能見到完整初始化狀態。
| 特性 | volatile | synchronized | Lock |
|---|---|---|---|
| 可見性 | ✅ | ✅ (釋放鎖前刷新) | ✅ |
| 有序性 | ✅ | ✅ | ✅ |
| 原子性 | ❌ | ✅ | ✅ |
| 阻塞 | 不阻塞 | 阻塞 | 可選 (tryLock) |
| 適用場景 | 狀態標志、單次賦值、DCL | 複雜臨界區、複合操作 | 高併發、可中斷、公平鎖 |
volatile int count; count++; 仍非線程安全(讀-改-寫三步),需用 AtomicInteger 或 synchronized。volatile boolean running)final volatile 針對對象引用不可變 + 狀態可見VarHandle.VOLATILE)