volatile不能保证count++原子性,因其仅保障单次读/写的可见性与有序性,而count++是“读—改—写”三步非原子操作,多线程下可能同时读取相同旧值并写回相同结果,导致数据丢失。

volatile 变量不能用于复合自增运算(比如 count++ 或 value += 1),根本原因在于:它只保障单次读或写的原子性与可见性,而自增操作本质上是“读—改—写”三步不可分割的动作,volatile 对中间步骤完全无保护能力。
volatile 保证什么?
它确保两点:
- 可见性:一个线程修改 volatile 变量后,新值会立即刷入主内存,其他线程读取时能拿到最新值;
- 有序性:禁止 JVM 和 CPU 对该变量相关的读写指令做重排序。
但它不提供任何锁机制,也不阻止其他线程在“读完还没写回”这个间隙插入操作。
自增操作实际被拆成三步
以 count++ 为例,JVM 实际执行的是:
立即学习“Java免费学习笔记(深入)”;
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 从主内存(或缓存)读取
count当前值到寄存器; - 寄存器中值加 1;
- 把新值写回主内存。
这三步不是原子的。即使 count 是 volatile 修饰的,线程 A 读到值为 100 后,还没来得及写回,线程 B 也读到了 100 —— 两个线程各自加 1 后都写回 101,最终结果只加了 1 次,而不是 2 次。
为什么 volatile++ 看似“读对了”,结果却错?
volatile 能保证第一步“读取”看到的是最新值,也能保证第三步“写回”确实发生;但第二步计算和写回之间没有排他性。多个线程可能基于同一个旧值做计算,造成更新丢失。这就是典型的竞态条件(race condition)。
什么情况下必须换方案?
遇到以下操作,volatile 就失效了,必须用其他手段:
- 计数器累加(
i++、sum += x); - 集合操作(
list.add()、map.put()); - 状态切换伴随逻辑判断(如“若未初始化则创建”);
- 多个变量需同步更新(如余额和交易时间)。
推荐替代方案:使用 AtomicInteger(基于 CAS)、synchronized 块,或 ReentrantLock。

















