volatile不保证复合操作原子性,因i++等操作含读-改-写三步,它仅确保单次读写可见性,无法防止中间步骤被干扰;需改用atomicinteger、synchronized或线程安全集合。

volatile 本身不限制你写非原子复合操作,它只是“不管”——你写了 i++、count += 1 或 list.add(x),编译器不会报错,JVM 也不会拦截。真正的问题在于:这些操作在多线程下必然出错,而 volatile 完全不提供保护。所以所谓“限制”,其实是靠开发者理解其边界、主动规避,而非语法或运行时强制。
为什么 volatile 对复合操作无效
因为复合操作(如 i++)本质是三步:
- 从主内存或工作内存读取
i的当前值 - 在 CPU 寄存器中执行加 1 计算
- 把新值写回内存
volatile 只保证第 1 步和第 3 步的单次读/写具有可见性,但无法阻止两个线程在第 2 步中间互相覆盖。结果就是“丢失更新”——10000 次自增后,i 可能只增加了 6000 多次。
哪些操作属于危险的非原子复合操作
只要涉及“读—改—写”链条,且中间状态可能被其他线程干扰,就不可用 volatile 保障。典型包括:
-
i++、--j、value += delta -
boolean flag = !flag(读旧值、取反、写新值) -
list.size() > 0 && list.remove(0)(两步间 list 可能已被修改) -
if (status == PENDING) status = PROCESSING(检查后再赋值,竞态条件)
正确替代方案
遇到上述场景,必须升级同步机制:
- 简单计数类操作 → 改用
AtomicInteger、AtomicLong等原子类,它们底层用 CAS 保证复合操作的原子性 - 多步骤逻辑或需锁保护的状态流转 → 用
synchronized块包裹整个临界区 - 集合操作 → 使用线程安全集合(如
ConcurrentHashMap、CopyOnWriteArrayList),或在外层加锁 - 状态检查+变更 → 考虑
AtomicReference.compareAndSet()或双重检查 + synchronized + volatile 组合(如 DCL 单例)
一个清晰的判断口诀
写代码前问自己一句:这个操作是否只做一次内存访问?
- 是 → 如
done = true、if (running)→ volatile 安全可用 - 否 → 如
counter++、config.updateIfAbsent(key, value)→ volatile 不行,必须换方案
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











