跳转到内容

volatile 与原子类

用途:决定“用 volatile 还是要用锁/原子类”时查阅

能力 是否提供
可见性(写立即刷主存,读从主存取) 是
有序性(禁止重排序跨越该变量) 是
原子性(复合操作如自增) 否
互斥(并发只有一个进入) 否

适用形态:一写多读的状态标志、安全发布的引用、作为后续加锁逻辑的哨兵(如 DCL 单例)。

不适用:任何“读-改-写”复合操作(count++)、需要多个变量保持一致性的场景。

java.util.concurrent.atomic 提供基于 CAS(compare-and-set)的无锁原子变量:AtomicInteger、AtomicLong、AtomicReference、AtomicStampedReference,以及高性能聚合类 LongAdder、Striped64。

类 适用
AtomicLong 需要精确值的计数器
LongAdder 高并发计数,允许最终一致(分段累加,冲突远小于 AtomicLong)
AtomicReference / AtomicMarkableReference 无锁替换对象引用
AtomicStampedReference 解决 CAS 的 ABA 问题(带版本戳)
字段更新器 AtomicXxxFieldUpdater 在不改类结构前提下对某字段做原子更新(现代代码少用)
  1. ABA 问题:值从 A 变 B 又回到 A,CAS 无法察觉。用版本戳(AtomicStampedReference)或不可变节点解决。
  2. 自旋开销:高冲突下反复失败重试,CPU 空转。冲突激烈时锁(synchronized/ReentrantLock)反而更优。
  3. 只能保证单变量原子性:多个变量的联合一致性仍需锁或不可变快照(把多字段封装进一个不可变对象,用 AtomicReference 整体替换)。
while (true) {
State cur = state.get();
State next = cur.tryAdvance();
if (next == null) return false; // 不允许迁移
if (state.compareAndSet(cur, next)) { publishEvent(); return true; }
}

要点:状态封装为不可变对象,迁移函数返回新对象,CAS 成功才产生副作用。若副作用在 CAS 前执行,失败重试会重复生效。

误区 修正
用 volatile 修饰数组元素或对象内部字段 volatile 只作用于引用本身,内部字段不受保护
用 AtomicInteger 做高并发统计 冲突严重时改 LongAdder
复合条件判断 + 更新用两次 CAS 合并为单变量整体替换或加锁
认为无锁一定比有锁快 高冲突场景自旋成本高于阻塞
忘记 ABA 涉及对象复用时用版本戳或避免复用
  • Doug Lea《The java.util.concurrent Synchronous Queue》等系列论文与 JSR-166 文档
  • 《Java 并发编程实战》第 11、15 章
  • OpenJDK java.util.concurrent.atomic 包文档