volatile 是 Java 提供的**輕量級同步機制**,保證兩大特性:
不保證原子性:volatile int i; i++; 仍非線程安全!
編譯器在 volatile 讀寫操作插入內存屏障:
效果等同於(但比 synchronized 輕量):
// 寫入
public synchronized void setValue(int value) { this.value = value; }
// 讀取
public synchronized int getValue() { return value; }
同時滿足以下條件才能安全使用 volatile:
// 正確:單線程寫入,多線程讀
volatile boolean shutdownRequested;
void shutdown() { shutdownRequested = true; } // 單線程調用
void work() { while (!shutdownRequested) { ... } } // 多線程讀
// 錯誤:lower <= upper 不變式涉及兩個變量
volatile int lower, upper;
void setBounds(int l, int u) { lower = l; upper = u; } // 競態條件!
public class TaskRunner {
private volatile boolean cancelled = false;
public void cancel() { cancelled = true; }
public void run() {
while (!cancelled) { // 立即可見取消信號
doWork();
}
}
}
public class Singleton {
private static volatile Singleton instance; // 關鍵!
public static Singleton getInstance() {
if (instance == null) { // 第一次檢查(無鎖)
synchronized (Singleton.class) {
if (instance == null) { // 第二次檢查(有鎖)
instance = new Singleton(); // 非原子:分配內存 -> 初始化 -> 賦值
}
}
}
return instance;
}
}
volatile 禁止指令重排,防止其他線程看到「半初始化」對象。
public class SensorReader {
private volatile double latestTemperature; // 單寫多讀
public void update(double temp) { latestTemperature = temp; }
public double getLatest() { return latestTemperature; }
}
| 誤區代碼 | 問題 | 修正 |
|---|---|---|
volatile int count; count++; | 複合操作非原子 | AtomicInteger 或 synchronized |
volatile int a, b; a = 1; b = 2; | 多變量無原子性 | 打包成對象或加鎖 |
volatile List list; list.add(x); | 引用可見 ≠ 內容可見 | 用 Collections.synchronizedList 或 CopyOnWriteArrayList |
| 以為 volatile 替代 synchronized | 語義不同,場景不同 | 理解 JMM,按需選擇 |
| 維度 | volatile | synchronized |
|---|---|---|
| 原子性 | 不保證 | 保證 |
| 可見性 | 保證 | 保證 |
| 有序性 | 保證 | 保證 |
| 阻塞 | 不阻塞 | 可能阻塞 |
| 適用粒度 | 單變量 | 代碼塊/方法 |
| 效能 | 較高(無鎖) | 較低(上下文切換) |
volatile 是「共享變量可見性」的專用工具,**不是萬能鑰匙**。正確使用前提:單寫入者、無不變式依賴、純狀態標誌。複雜協同場景請使用 synchronized、Lock 或並發工具類。