volatile适合做开关,当变量仅作通知用途且写操作为独立赋值(如running = false、state = STOPPED)时,能保证修改对所有线程立即可见;但不适用于count++等读-改-写复合操作。

volatile 能让共享变量的修改对所有线程立即可见,适合做轻量级的“开关”(比如启动/停止标志),但它不能保证原子性,所以只适用于单次读或写的简单状态控制。
什么时候适合用 volatile 做开关?
当变量只承担“通知作用”,且写操作是独立、无依赖的赋值时,比如:
- 一个布尔标志
running = true表示线程应继续执行; - 一个整型状态
state = STOPPED表示当前模块已关闭; - 不涉及
count++、flag = !flag这类读-改-写操作。
怎么正确声明和使用 volatile 开关?
声明时加 volatile 关键字,确保每次读写都直接走主内存:
public class TaskRunner {
private volatile boolean running = true;
public void run() {
while (running) {
// 执行任务...
doWork();
}
System.out.println("Stopped.");
}
public void stop() {
running = false; // 立即对其他线程可见
}
}
注意:running = false 是原子写,while(running) 是原子读,组合起来就能安全协作。
立即学习“Java免费学习笔记(深入)”;
volatile 不能替代 synchronized 的情况
以下操作即使用了 volatile,依然可能出错:
-
counter++:本质是读+加1+写三步,volatile 不保证这三步整体原子; -
if (flag) { flag = false; doSomething(); }:判断和修改之间存在竞态窗口; - 多个变量需保持一致(如
data和ready),volatile 无法保证它们的写顺序可见性(需用final字段或锁)。
配合 happens-before 理解可见性
volatile 写操作 happens-before 后续任意线程对该变量的 volatile 读操作。这意味着:
- 线程 A 执行
running = false; - 线程 B 后续执行
while (running)时,不仅能看到running的最新值,还能看到 A 在写之前写入的其他变量(只要没被编译器/JIT 重排序干扰)。
这是 volatile 提供“轻量同步”的核心机制,也是它能当开关用的底层保障。


















