AtomicInteger等类解决单变量“读-改-写”竞态问题,不替代锁或解决所有并发问题;i++非原子致丢失更新,volatile无法修复;CAS实现原子操作,但不支持多变量事务或复杂条件逻辑。

Java 的 AtomicInteger、AtomicLong 等类,解决的是**多线程环境下对单个变量做“读-改-写”操作时的竞态条件问题**,而不是替代锁或解决所有并发问题。
为什么 i++ 在多线程下会出错
看似简单的 i++ 实际包含三步:读取当前值 → 加 1 → 写回内存。若两个线程同时执行,可能都读到旧值(比如 5),各自加 1 后都写回 6,结果丢失一次更新。
这种非原子性行为无法靠 volatile 修复——它只保证可见性,不保证操作的原子性。
-
volatile int i = 0;不能防止i++的中间步骤被穿插 -
AtomicInteger i = new AtomicInteger(0);的i.incrementAndGet()是底层用 CPU 的CAS(Compare-And-Swap)指令实现的单指令原子操作 - 不是所有平台都支持 CAS,但 HotSpot JVM 在 x86/ARM 上已通过
lock cmpxchg或ldxr/stxr等指令兜底
Atomic 类能做什么,不能做什么
它们适用于「单变量、无依赖、无副作用」的原子更新场景,比如计数器、序列号生成、状态标志位切换。
立即学习“Java免费学习笔记(深入)”;
一旦涉及多个变量联动(如转账:A 减、B 增),或需要复合逻辑判断(如“只有余额 > 100 才扣款”),AtomicXxx 就力不从心了——它不提供条件式多步原子操作,也没有内置重试机制。
- ✅ 支持:自增、自减、CAS 设置、getAndSet、getAndUpdate、updateAndGet
- ❌ 不支持:跨字段事务、阻塞等待、公平性控制、超时重试
- ⚠️ 注意:
getAndUpdate和updateAndGet接收的UnaryOperator函数体里不应有 I/O、锁或耗时操作,否则会卡住 CAS 自旋
AtomicReference 与对象引用的“原子性”边界
AtomicReference 保证的是「引用本身」的读写原子性,不是它指向的对象内部状态的线程安全。
比如用 AtomicReference 存一个 >
ArrayList,你用 compareAndSet 替换整个引用没问题;但若调用 get().add("x"),这个 add 操作仍需额外同步——因为 ArrayList 本身不是线程安全的。
- 常见误用:
atomicRef.get().size()返回后,对象可能已被其他线程修改,结果不可靠 - 正确姿势:尽量把状态封装进不可变对象,再用
AtomicReference做整体替换 - 复杂状态变更建议配合
StampedLock或ReadWriteLock,而非强行拆成多个 Atomic 字段
性能开销和 ABA 问题的真实影响
CAS 自旋在低竞争时比锁快很多,但高竞争下可能反复失败重试,反而更耗 CPU。JVM 对 AtomicXxx 有专门优化(如内联、消除冗余屏障),所以不要自己用 Unsafe 模拟。
ABA 问题确实存在:变量从 A→B→A,CAS 误判为“没变过”,但实际上中间状态已改变。但现实中绝大多数场景(计数器、开关标志)根本不在乎中间是否绕过 B;真需要严格版本控制的,应使用 AtomicStampedReference 或直接上 LongAdder / ConcurrentHashMap 等更高层工具。
- 除非你在写无锁栈/队列这类底层结构,否则基本不用操心 ABA
-
LongAdder在高并发计数场景比AtomicLong更高效,但它不提供精确的实时值(最终一致性) - 别为了“看起来更原子”而滥用
AtomicBoolean控制流程——有时一个synchronized块更清晰、更易维护
真正难的从来不是选哪个 Atomic 类,而是判断「这里到底需不需要原子性」,以及「原子性边界划在哪」——变量级?对象级?还是业务语义级?









