volatile 与原子类
用途:决定“用 volatile 还是要用锁/原子类”时查阅
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 |
在不改类结构前提下对某字段做原子更新(现代代码少用) |
CAS 的三个固有局限
「CAS 的三个固有局限」小节链接- ABA 问题:值从 A 变 B 又回到 A,CAS 无法察觉。用版本戳(
AtomicStampedReference)或不可变节点解决。 - 自旋开销:高冲突下反复失败重试,CPU 空转。冲突激烈时锁(
synchronized/ReentrantLock)反而更优。 - 只能保证单变量原子性:多个变量的联合一致性仍需锁或不可变快照(把多字段封装进一个不可变对象,用
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包文档