volatile 變數對所有執行緒立即可見(強制刷新至主記憶體)。volatile 讀寫與前後指令重排(happens-before 語義)。n++、n = n + 1、**讀取-修改-寫入** 任何多步驟操作**不是原子**的。public class VolatileCounter extends Thread {
public static volatile int n = 0; // volatile 修飾
@Override
public void run() {
for (int i = 0; i < 10; i++) {
n = n + 1; // 非原子:讀取 n → 加 1 → 寫回
try { Thread.sleep(1); } catch (InterruptedException ignored) {}
}
}
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) threads[i] = new VolatileCounter();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
System.out.println("n = " + n); // 預期 1000,實際常見 800~999
}
}
原因:volatile 只保證單次讀/寫的可見性,n = n + 1 包含三個位元碼指令,執行緒切換發生在中間時,更新會丟失。
| 方案 | 程式碼 | 原理 | 效能 | 適用場景 |
|---|---|---|---|---|
| synchronized 方法 | static synchronized void inc() { n++; } | 鎖 Class 物件,進入互斥 | 中 | 簡單同步、低爭用 |
| AtomicInteger (推薦) | AtomicInteger n = new AtomicInteger(); n.incrementAndGet(); | CAS 無鎖硬體指令 | 高 | 高併發計數器、序號生成 |
| LongAdder (Java 8+) | LongAdder n = new LongAdder(); n.increment(); | 分段計數器,極低爭用 | 極高 | 統計計數、高併發累加 |
| volatile + CAS 迴圈 | 手寫 UNSAFE.compareAndSwapInt | 同上,但複雜易錯 | 高 | 教學/特殊需求 |
public class WorkerThread extends Thread {
private volatile boolean shutdown = false; // 只有主執行緒寫,工作執行緒讀
public void requestShutdown() { shutdown = true; }
@Override
public void run() {
while (!shutdown) { // 保證能看見最新值
doWork();
}
cleanup();
}
}
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 時,instance = new Singleton() 可能被重排為:分配記憶體 → 指派參考 → 執行建構子,導致其他執行緒看到「非 null 但未初始化完成」的物件。
public class EventSource {
private volatile boolean eventOccurred = false;
public void trigger() { eventOccurred = true; }
public boolean checkAndClear() {
if (eventOccurred) {
eventOccurred = false; // 單一寫入者(消費者)
return true;
}
return false;
}
}
| 模式 | 程式碼 | 問題 | 修正 |
|---|---|---|---|
| 計數器 | volatile int n; n++; | 複合操作非原子 | AtomicInteger |
| 檢查後行動 | if (volatileFlag) doSomething(); | 檢查與行動間有間隙 | synchronized 或 AtomicReference |
| 多變數不變條件 | volatile int x, y; // 需 x > y | 無法原子更新兩者 | synchronized 保護兩者 |
| 陣列/集合元素 | volatile int[] arr; arr[0]++; | volatile 只保護參考,不保護元素 | AtomicIntegerArray 或鎖 |
寫入 volatile 變數 ----happens-before----> 讀取同一 volatile 變數
// 這意味著:寫入前的所有記憶體操作(包括非 volatile 變數),
// 對讀取該 volatile 變數的執行緒都是可見的。
這是 volatile 能作為「發布」機制的理論基礎(如雙重檢查鎖、物件安全發布)。
Atomic*、Concurrent*、synchronized。AtomicIntegerArray、CopyOnWriteArrayList 或鎖。n++、check-then-act、多變數不變條件。final volatile 無意義(final 已保證初始化可見性),但 volatile 參考指向的物件內部欄位若需可見性,該物件內部欄位也需 volatile 或 final。一句話總結:volatile 是「輕量級可見性保證」,不是「輕量級鎖」;用在旗標發布、單例雙重檢查,計數器請直接用 AtomicInteger。