volatile 是 Java 並發編程中輕量級的同步機制,提供可見性與有序性保證,但不保證原子性。本文深度解析 JMM 語義、happens-before 規則、適用場景模式、進階用法與常見誤區。
// 寫入屏障
StoreStore
volatile write
StoreLoad
// 讀取屏障
LoadLoad
volatile read
LoadStore
| 特性 | volatile | synchronized |
|---|---|---|
| 可見性 | ✅ | ✅ |
| 有序性 | ✅ | ✅ |
| 原子性 | ❌ | ✅ |
| 阻塞 | 不阻塞 | 可能阻塞 |
| 適用場景 | 狀態標誌、單次發布、獨立變量 | 複合操作、臨界區、多變量不變式 |
| 性能 | 輕量 | 較重 (鎖升級優化後仍有開銷) |
volatile 只保證單個變量操作的可見性與有序性synchronized 保證代碼塊內所有變量的原子性、可見性、有序性volatile 無法替代 synchronized 實現 i++ 等複合操作JMM 定義的 8 條 happens-before 規則中,第 3 條專門針對 volatile:
volatile 變量規則:對一個 volatile 變量的寫入操作,happens-before 於後續對該變量的讀取操作。
class VolatileHB {
int x = 0;
volatile boolean ready = false;
// 線程 A
public void writer() {
x = 42; // 1. 普通寫入
ready = true; // 2. volatile 寫入
}
// 線程 B
public void reader() {
if (ready) { // 3. volatile 讀取
assert x == 42; // 4. 普通讀取,必見 42
}
}
}
// 1 hb 2 (程序順序)
// 2 hb 3 (volatile 規則)
// 3 hb 4 (程序順序)
// 由傳遞性:1 hb 4,故 x=42 對線程 B 可見
public class ShutdownFlag {
private volatile boolean shutdownRequested = false;
public void requestShutdown() {
shutdownRequested = true; // 發布關閉信號
}
public void doWork() {
while (!shutdownRequested) { // 輪詢檢查
// 執行任務
}
}
}
public class ResourceHolder {
private volatile Resource resource; // 只有寫入一次
public void init() {
Resource r = new Resource(); // 構造過程
resource = r; // volatile 寫入:發布完全構造的對象
}
public void use() {
Resource r = resource; // volatile 讀取
if (r != null) {
r.doSomething(); // 安全訪問
}
}
}
public class SensorMonitor {
private volatile double temperature; // 定期更新
// 後台線程:每秒採樣
@Scheduled(fixedRate = 1000)
public void sample() {
temperature = sensor.read(); // 單一寫入者
}
// 業務線程:隨時讀取最新值
public double getTemperature() {
return temperature; // 多讀取者
}
}
@ThreadSafe
public class ConfigBean {
private volatile String url;
private volatile int timeout;
private volatile boolean enabled;
// 只有簡單 getter/setter,無邏輯
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
// ...
}
@ThreadSafe
public class CheesyCounter {
@GuardedBy("this") private volatile int value; // 寫入受鎖保護
public int getValue() { // 讀取無鎖,volatile 保證可見
return value;
}
public synchronized int increment() { // 寫入加鎖
return value++;
}
}
| 誤區 | 錯誤代碼 | 正確方案 |
|---|---|---|
| volatile 保證 i++ 原子 | volatile int count; count++; | AtomicInteger / LongAdder / synchronized |
| volatile 陣列元素可見 | volatile int[] arr; arr[0] = 1; | 只保證引用可見,元素需 AtomicIntegerArray |
| volatile 替代 synchronized | 復雜業務邏輯用 volatile | 多變量不變式必須用鎖 |
| 雙重檢查鎖無 volatile | instance = new Singleton(); | JDK 5+ 必須加 volatile |
| volatile 長/雙原子 | volatile long x; (32位 JVM) | JDK 5+ 保證 64 位原子,但複合操作仍非原子 |
// 寫入屏障:禁止上方操作移到 volatile write 之後
// 讀取屏障:禁止下方操作移到 volatile read 之前
// 示例
int a = 0;
volatile boolean flag = false;
public void writer() {
a = 1; // 1
flag = true; // 2 (volatile write)
// 禁止 1 移到 2 之後
}
public void reader() {
if (flag) { // 3 (volatile read)
int x = a; // 4
// 禁止 4 移到 3 之前
}
}
// 底層實現 (JDK 8+)
public final native void putVolatile(Object o, long offset, int x); // Unsafe
public final native int getVolatile(Object o, long offset); // Unsafe
// JDK 9+ VarHandle (推薦)
private static final VarHandle VALUE;
static {
try {
VALUE = MethodHandles.lookup()
.in(VolatileClass.class)
.findVarHandle(VolatileClass.class, "value", int.class);
} catch (Exception e) { throw new ExceptionInInitializerError(e); }
}
public void setValue(int v) { VALUE.setVolatile(this, v); }
public int getValue() { return (int) VALUE.getVolatile(this); }
volatile 是 Java 內存模型中可見性與有序性的基石。核心認知:不保證原子性、適用單一寫入/獨立觀察/一次性發布、禁止指令重排序、輕量級但易誤用。現代並發編程優先選擇:不可變對象 → 併發容器 → 原子類 → Lock → volatile → synchronized。理解 happens-before、內存屏障、重排序規則,是掌握 Java 並發本質的關鍵。