volatile 做什麼?
volatile 是 Java 提供的輕量級同步機制,針對成員變量提供兩大語義保證:
- 可見性:寫入後立即刷新到主記憶體,讀取時強制從主記憶體獲取,確保所有執行緒看到最新值
- 有序性(禁止指令重排):編譯器/CPU 不會將 volatile 讀寫與其前後操作重排序 (JDK 1.5+ JSR-133)

底層原理:內存屏障
- 寫入 volatile:StoreStore + StoreLoad 屏障 → 確保前面寫入不被重排到 volatile 寫之後,且 volatile 寫刷新到主存
- 讀取 volatile:LoadLoad + LoadStore 屏障 → 確保 volatile 讀後的讀/寫不被重排到之前,且從主存讀取最新值
volatile vs synchronized
| 維度 | volatile | synchronized |
| 原子性 | ❌ 單次讀/寫原子,複合操作 (i++) 非原子 | ✅ 互斥鎖保證臨界區原子性 |
| 可見性 | ✅ | ✅ (鎖釋放前刷新、獲取後失效本地緩存) |
| 有序性 | ✅ | ✅ |
| 阻塞 | ❌ 無阻塞 | ✅ 可能阻塞 |
| 適用場景 | 狀態標記、單次賦值、雙重檢查鎖 | 複雜臨界區、複合操作 |
經典適用場景
1. 狀態標記位
volatile boolean shutdown = false;
void shutdown() { shutdown = true; }
void work() { while (!shutdown) { /* do task */ } }
2. 雙重檢查鎖 (DCL) 修正
class Singleton {
private volatile static Singleton instance; // 關鍵 volatile
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) instance = new Singleton();
}
}
return instance;
}
}
3. 延遲初始化 (單次安全發布)
class Config {
private volatile Helper helper = null;
public Helper getHelper() {
Helper h = helper;
if (h == null) synchronized(this) { h = helper; if (h==null) helper = h = new Helper(); }
return h;
}
}
常見誤區與避坑
- 以為 volatile 能保證 i++ 原子 → 錯誤!需用
AtomicInteger 或 synchronized
- 濫用 volatile 替代 synchronized → 只適合「單次寫入、多線程讀取」或「狀態標記」,複雜邏輯仍需鎖
- final 引用的物件內部可變 →
final List list = new ArrayList(); list.add(1); 其他線程不可見,需配合 volatile 或不可變集合
- long/double 非原子讀寫 → 32 位 JVM 下 64 位讀寫非原子,
volatile long 可保證原子
效能考量
volatile 禁用 CPU 緩存優化,頻繁讀寫會影響效能。僅在必要時使用,單純讀多寫少場景優先考慮 synchronized 或無鎖資料結構。
- 上一篇文章: 手寫 Java 編譯器系列(一):Thompson 構造法之輸入文本預處理與宏展開
- 下一篇文章: Java 多線程與併發核心知識體系總結:從基礎概念到面試高頻題