volatile适合状态标记,因其仅需保证布尔值等简单读写的可见性与原子性;但不适用于flag = !flag、counter++等含读-改-写三步的复合操作,因无法保障整体原子性。

volatile 能安全用于多线程共享的运行状态标记(如 running、shutdownRequested),但仅限于“单纯读写布尔值或基本类型变量”这类简单场景——它不保证复合操作的原子性,也不解决竞态条件,只确保可见性和禁止指令重排序。
为什么 volatile 适合状态标记
状态标记通常只需满足两点:
- 一个线程修改后,其他线程能立即看到新值(可见性);
- 读写本身是原子操作(boolean、int、long 等在 JVM 中对齐后读写天然原子)。
volatile 正好提供这两点:JVM 会强制每次读都从主内存加载,每次写都立即刷回主内存,并插入内存屏障防止相关指令被重排序。
正确用法示例
以下是一个典型的使用 volatile 控制线程生命周期的例子:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 定义为
private volatile boolean running = true; - 工作线程循环中用
while (running) { ... }判断退出条件 - 外部线程调用
running = false;发起停止请求
这样,一旦 running 被设为 false,工作线程下一次检查时必然看到该值,不会因 CPU 缓存旧值而无限循环。
不能用 volatile 的情况
以下操作看似简单,实则涉及多个步骤,volatile 无法保证原子性,必须改用 AtomicBoolean 或加锁:
-
flag = !flag;—— 读 + 取反 + 写,三步非原子 -
counter++;—— 读 + 加 1 + 写,非原子(即使counter是 volatile int) - 依赖当前值做条件更新,例如:
if (status == READY) status = PROCESSING;
和 synchronized / AtomicBoolean 怎么选
选 volatile 当且仅当:变量只被单个线程写,多个线程只读(或最多一个线程写、其余只读),且读写不构成复合逻辑。
- 比
synchronized开销更低,无阻塞、无上下文切换 - 比
AtomicBoolean更轻量(后者底层仍用 volatile + CAS,但提供了原子方法) - 若后续可能扩展为原子操作(如“设为 true 仅当原值为 false”),建议直接用
AtomicBoolean,避免重构
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










