當只需同步讀寫一兩個實例域時,synchronized 開銷過大。volatile 提供無鎖的同步訪問機制,是輕量級線程協作利器。掌握它需先理解 Java 內存模型(JMM)與并发三大特性。
long/double 除外)是原子的。x=10 原子;y=x、x++、x=x+1 非原子(含讀、改、寫多步)。java.util.concurrent.atomic 包提供原子類(AtomicInteger 等),基於 CAS 指令實現高效原子操作。volatile 變量:寫入強制刷新主內存,讀取強制從主內存加載,保證可見性。volatile 禁止指令重排序,提供有序性保證。synchronized/Lock 通過互斥串行執行也保證有序性。volatile 變量前的操作,結果已完成且對前序操作可見;volatile 變量後的操作,確未執行;volatile 訪問語句與前後語句重排。public class Test {
public volatile int inc = 0;
public void increase() { inc++; } // 非原子:讀-改-寫
public static void main(String[] args) {
final Test test = new Test();
for (int i=0; i<10; i++)
new Thread(() -> { for (int j=0; j<1000; j++) test.increase(); }).start();
while (Thread.activeCount() > 1) Thread.yield();
System.out.println(test.inc); // 輸出 < 10000
}
}
多線程並發 inc++ 導致更新丟失。volatile 不保證復合操作原子性。
i++、i-- 等)。public class NumberRange {
private volatile int lower, upper;
public void setLower(int v) { if (v > upper) throw ...; lower = v; }
public void setUpper(int v) { if (v < lower) throw ...; upper = v; }
}
線程 A setLower(4)、線程 B setUpper(3) 並發執行,可能通過檢查但最終範圍變為 (4,3),違反 lower <= upper 不變式。此處必須用 synchronized。
volatile boolean shutdownRequested;
public void shutdown() { shutdownRequested = true; }
public void doWork() { while (!shutdownRequested) { /* do stuff */ } }
標記不依賴其他狀態,volatile 簡化同步代碼,性能優於 synchronized。
public class Singleton {
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) instance = new Singleton();
}
}
return instance;
}
}
volatile 禁止 new Singleton() 指令重排(分配內存→初始化→賦值引用),防止其他線程看到半初始化對象。雖有微小性能損耗,但保證正確性。
推薦替代:靜態內部類單例(線程安全、延遲初始化、無鎖、代碼簡潔)。
volatile 是輕量級同步機制,嚴格遵守「不依賴舊值、無不變式」兩條件時可替代 synchronized。最常用於狀態標記、DCL 單例。其他場景請優先使用 synchronized 或 JUC 工具類,避免微妙並發 Bug。