volatile仅保证可见性和禁止指令重排序,不保证原子性;适用于单写多读、写不依赖当前值的状态标志等场景,不能替代锁或用于复合操作。

volatile 不是锁,也不能替代锁。它只解决可见性和重排序问题,对原子性完全无效。在复杂业务逻辑中,一旦把 volatile 当成“轻量级 synchronized”来用,就极易引发隐蔽的竞态条件——这类错误往往在线上低概率复现,极难定位。
明确 volatile 的能力边界
它只做三件事:
- 每次读取都从主内存拿最新值(不缓存)
- 每次写入都立即刷回主内存(不滞留工作内存)
- 在读/写前后插入内存屏障,阻止相关指令重排
但它不阻止多个线程同时进入同一段代码,也不保证「读-改-写」这类操作的中间状态不被覆盖。比如 counter++、list.add(x)、map.put(k, v),哪怕变量本身是 volatile,这些操作依然不是原子的。
识别哪些操作看似简单实则危险
以下写法在业务代码中很常见,但全部踩中 volatile 的盲区:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
-
volatile int retryCount = 0;→ 后续执行retryCount++:三步非原子,可能丢更新 -
volatile List<string> logs = new CopyOnWriteArrayList();</string>→logs引用是 volatile,但logs.add("msg")的内部逻辑仍需自身线程安全,volatile 对它无约束 -
volatile boolean isProcessing = true;→ 后续配if (isProcessing) doWork();:这里没问题;但若改成if (isProcessing) { isProcessing = false; doWork(); },第二行赋值虽可见,却无法防止两个线程同时通过 if 判断并执行后续逻辑
用原子类或锁代替“伪原子”场景
当业务逻辑涉及「检查+变更」或「多步协同」时,必须升级同步手段:
- 计数器、累加器 → 改用
AtomicInteger、AtomicLong,它们底层用 CAS 保障原子性 - 共享集合操作 → 直接选用线程安全实现(如
ConcurrentHashMap、CopyOnWriteArrayList),或用synchronized块保护临界区 - 状态机流转(如订单从「待支付」→「已支付」→「已发货」)→ 用
AtomicReference<orderstatus></orderstatus>+ CAS 自旋,或配合状态校验逻辑 - 需要阻塞等待、条件通知、可中断等高级语义 → 必须上
ReentrantLock或Condition
给 volatile 留出真正适合它的位置
它最自然的用武之地是「单向、一次性、无依赖」的状态信号:
- 启动/停止开关:
volatile boolean running = true;,worker 线程循环检查,外部调用方设为 false 即可优雅退出 - 初始化完成标志:
volatile boolean initialized = false;,初始化线程末尾置 true,其他线程轮询等待 - DCL 单例中的 instance 字段:
private volatile static Singleton instance;,确保对象构造完成且引用发布后,其他线程能看到完整初始化的对象
只要变量的读写不依赖当前值,也不影响其他变量状态,volatile 就是清晰、高效、安全的选择。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










