關鍵誤區:volatile 不保證原子性!複合操作(如 count++)仍需同步或原子類。
JVM 運行時資料區:
操作流程:
read + load:從主記憶體讀取到工作記憶體use + assign:在工作記憶體執行運算(可多次)store + write:寫回主記憶體問題:非 volatile 變數,線程 A 寫回主記憶體的時間不可控,線程 B 可能仍使用舊副本。
volatile 保證:每次 read 必從主記憶體獲取最新值;每次 write 立即刷回主記憶體。
public class Counter {
public volatile static int count = 0; // 加了 volatile
public static void inc() {
try { Thread.sleep(1); } catch (InterruptedException ignored) {}
count++; // 非原子:read-load-use-assign-store-write
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) new Thread(Counter::inc).start();
Thread.sleep(2000);
System.out.println("結果:" + count); // 多次運行:992, 995, 998... 從未 1000
}
}
原因:count++ 拆解為三步驟,volatile 只保證每步的可見性,不保證三步為原子單元。線程間交錯導致更新丟失。
| 方案 | 代碼 | 適用場景 |
|---|---|---|
| synchronized | synchronized(Counter.class){ count++; } | 簡單、低爭用 |
| AtomicInteger | AtomicInteger count = new AtomicInteger(); count.incrementAndGet(); | 高併發計數器、CAS 無鎖 |
| LongAdder | LongAdder count = new LongAdder(); count.increment(); | 極高併發、統計計數 |
volatile boolean shutdown = false;
void shutdown() { shutdown = true; }
void work() { while (!shutdown) { ... } }
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) instance = new Singleton();
}
}
return instance;
}
volatile 禁止 new Singleton() 的指令重排(分配記憶體 → 初始化物件 → 賦值引用),避免其他線程看到「半初始化」物件。
volatile Config config = null;
Config getConfig() {
Config c = config;
if (c == null) synchronized (this) { c = config; if (c == null) config = c = new Config(); }
return c;
}
| 維度 | volatile | synchronized |
|---|---|---|
| 原子性 | ✗ | ✓ |
| 可見性 | ✓ | ✓ |
| 有序性 | ✓ | ✓ |
| 阻塞 | 不阻塞 | 可能阻塞 |
| 適用對象 | 變數 | 方法/代碼塊 |
| 效能 | 較高(無鎖) | 較低(上下文切換) |
JIT 編譯器為 volatile 讀寫插入內存屏障:
StoreStore + StoreLoad(確保寫入前操作不重排到寫入後、寫入立即刷新)LoadLoad + LoadStore(確保讀取後操作不重排到讀取前)volatile 是輕量級同步機制,解決可見性與有序性synchronized、java.util.concurrent.atomic、LongAdder、StampedLock 等