原子类依赖jmm通过volatile字段保证可见性与有序性,并基于cpu的cas指令实现无锁原子操作;需注意组合操作非原子、aba问题及适用边界。

Java 内存模型(JMM)通过定义线程间共享变量的可见性、有序性和原子性规则,为无锁编程提供了理论基础。原子类(如 AtomicInteger、AtomicReference 等)正是基于 JMM 和底层 CPU 的 CAS(Compare-and-Swap)指令实现的线程安全操作,无需 synchronized 或锁机制。
原子类如何依赖 JMM 保证可见性与有序性
JMM 规定:对 volatile 变量的读写具有“先行发生”(happens-before)关系,且禁止重排序。原子类内部字段被声明为 volatile,例如 AtomicInteger.value 是 volatile int。这确保了:
- 一个线程对原子变量的修改,能立即被其他线程看到(可见性);
- CAS 操作前的读取和操作后的写入,不会被编译器或处理器乱序执行(有序性);
- volatile 读写还建立了内存屏障,阻止屏障两侧的指令重排。
CAS 是原子类无锁的核心机制
原子类的 increment、compareAndSet 等方法最终调用 Unsafe 类的 native CAS 指令。该指令在硬件层面保证“读-改-写”三步不可分割:
- 读取当前值;
- 比较是否等于预期值;
- 若相等,则更新为新值,返回 true;否则返回 false,不修改。
整个过程由 CPU 直接支持(如 x86 的 cmpxchg),无需操作系统介入,也没有阻塞开销。但要注意 ABA 问题——值从 A→B→A,CAS 会误认为未变;可用 AtomicStampedReference 带版本号解决。
正确使用原子类的关键实践
无锁 ≠ 无脑用。需注意组合操作的原子性边界:
- 单个原子方法(如
getAndIncrement())是线程安全的; - 多个原子操作连用(如先
get()再set())不构成原子块,可能被其他线程穿插执行; - 复杂逻辑应优先考虑
compareAndSet()循环重试,而非拆解为独立 get/set; - 避免在原子类上加锁(如 synchronized(this)),会破坏无锁设计初衷,甚至引发死锁风险。
适用场景与局限性
原子类适合状态简单、竞争不激烈的计数器、标志位、轻量级状态机等:
- 高并发下 CAS 失败重试可能导致 CPU 浪费(自旋开销),极端情况下不如锁稳定;
- 无法替代锁处理临界区内多变量协同更新(如银行转账需同时扣减 A 账户、增加 B 账户);
- 对象引用类(
AtomicReference)可配合乐观锁思想实现无锁链表、栈等数据结构,但实现复杂度显著上升。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











