volatile不能保证原子性,因其仅保障单次读/写的可见性与有序性,无法确保“读—改—写”等多步复合操作的不可分割性;如i++会拆解为读取、计算、写回三步,线程可能交叉执行导致结果丢失。

volatile 变量不能保证原子性,是因为它只管“单次读”或“单次写”的可见与有序,不管“读—改—写”这类需要多个步骤连贯执行的操作。
它只保证每次读写都直达主内存
当一个变量被声明为 volatile,JVM 会确保:
- 每次读取该变量时,都从主内存加载最新值,不使用线程本地缓存;
- 每次写入该变量后,立即刷回主内存,并让其他线程的缓存失效;
- 插入内存屏障,禁止编译器和处理器对该变量的读写做重排序。
但这仅作用于“一次读”或“一次写”本身——比如 flag = true 或 int x = count 是原子的;而 count++ 不是。
i++ 这种操作天然就不是原子的
i++ 看似一条语句,实际对应三步独立动作:
立即学习“Java免费学习笔记(深入)”;
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 从主内存读取 i 的当前值(比如是 5);
- 在 CPU 寄存器中加 1,得到 6;
- 把结果 6 写回主内存。
volatile 能保证第 1 步读到的是最新值、第 3 步写完立刻被别人看到,但无法阻止两个线程在第 1 步同时读到 5,各自算出 6,再先后写回去——最终结果还是 6,丢了一次累加。
底层没有锁或硬件级原子指令支持
volatile 编译后通常加入 LOCK 前缀或内存屏障指令(如 LoadStore),这些指令能控制顺序、刷新缓存,但不提供互斥访问能力,也不调用 CPU 的 CAS(Compare-And-Swap)或 XADD 等原子指令。
相比之下,AtomicInteger.incrementAndGet() 能做到原子自增,是因为它底层调用的是 CAS 循环:先读、再比、再换,整个过程由硬件保证不可打断。
什么时候用 volatile 才安全
适合那些“只写不依赖旧值”或“只读不修改”的场景:
- 状态标志位:如 volatile boolean shutdown = false;,一个线程设为 true,其他线程立即感知;
- 双重检查单例中的 instance 字段,防止指令重排导致未初始化对象被引用;
- 配合其他同步机制做轻量通知,比如作为“数据就绪”的信号。
只要涉及“读取当前值 → 计算新值 → 写回”这个链条,就必须换 synchronized、ReentrantLock 或原子类来保障整体原子性。

















