| 語義 | 描述 | 實現機制 |
|---|---|---|
| 可見性 | 一線程寫入,其他線程立即可見 | 寫入時刷新到主存,讀取時從主存加載 |
| 有序性 | 禁止指令重排序 | 內存屏障 |
| 非原子性 | 復合操作 (i++) 仍不安全 | 需配合 synchronized/AtomicInteger |
// JMM 插入的內存屏障
volatile write:
StoreStore // 確保前面普通寫入不被重排到 volatile 寫之後
StoreLoad // 核心:強制刷新寫緩衝區到主存,並使其他 CPU 緩存失效
volatile read:
LoadLoad // 確保 volatile 讀不被重排到後面普通讀之前
LoadStore // 確保 volatile 讀不被重排到後面普通寫之前
// ❌ 問題代碼:可能無限循環
class TaskRunner {
private boolean stop = false; // 非 volatile
public void run() {
while (!stop) { doWork(); } // JIT 可能優化為 if(!stop) while(true)
}
public void stop() { stop = true; }
}
// ✅ 修正:volatile 保證可見性 + 禁止重排
class TaskRunner {
private volatile boolean stop = false;
public void run() {
while (!stop) { doWork(); }
}
public void stop() { stop = true; } // 寫入觸發 StoreLoad,其他 CPU 緩存失效
}
public class Singleton {
// volatile 關鍵:禁止 instance = new Singleton() 的指令重排
// 1. 分配內存 2. 初始化對象 3. instance 指向內存
// 無 volatile 時,可能發生 1→3→2,導致其他線程獲取到半初始化對象
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次檢查 (無鎖,高性能)
synchronized (Singleton.class) {
if (instance == null) { // 第二次檢查 (線程安全)
instance = new Singleton();
}
}
}
return instance;
}
}
class ConfigManager {
private volatile boolean initialized = false;
private Config config;
// 線程 A:初始化
public void init() {
Config newConfig = loadConfig(); // 耗時操作
config = newConfig;
initialized = true; // volatile 寫:StoreLoad 保證 config 可見
}
// 線程 B:使用
public Config getConfig() {
while (!initialized) { // volatile 讀:LoadLoad 保證讀到最新 config
Thread.yield(); // 或 sleep
}
return config; // 保證看到完整初始化的 config
}
}
| 特性 | volatile | synchronized | Lock |
|---|---|---|---|
| 可見性 | ✅ | ✅ | ✅ |
| 有序性 | ✅ | ✅ | ✅ |
| 原子性 | ❌ (單讀/寫除外) | ✅ | ✅ |
| 阻塞 | ❌ | ✅ | ✅ |
| 適用場景 | 狀態標記、DCL、獨立變量 | 臨界區、復合操作 | 複雜同步、可中斷、公平性 |
private volatile int count = 0;
public void increment() { count++; } // 非原子!讀-改-寫三步操作
// 正確:AtomicInteger 或 synchronized
private volatile int[] arr = new int[10];
arr[0] = 1; // 只保證 arr 引用可見,不保證元素寫入可見
// 正確:AtomicIntegerArray 或 synchronized
// JDK 5+ 已保證 64 位讀寫原子性,但非 volatile 仍有可見性問題
private volatile long timestamp; // 正確:保證可見性 + 原子性
public class ImmutableConfig {
private final String url; // final 語義:構造函數結束前寫入可見
private final int timeout;
private final Map params; // 容器需不可變
public ImmutableConfig(String url, int timeout, Map params) {
this.url = url;
this.timeout = timeout;
this.params = Collections.unmodifiableMap(new HashMap<>(params));
}
// 發布時無需 volatile,final 保證安全發布
// 但若引用本身可變,仍需 volatile
private volatile ImmutableConfig currentConfig;
}
volatile 是 Java 併發編程的「輕量級同步工具」:
掌握 volatile 的邊界,是寫出正確高併發代碼的關鍵一步。