AtomicInteger通过CAS与volatile协同实现线程安全:Unsafe调用CPU原子指令(如x86的cmpxchg),结合valueOffset定位内存地址、volatile保证可见性,compareAndSet执行原子比较替换,getAndIncrement则通过循环重试完成乐观锁更新。

要分析 AtomicInteger 底层的 CAS 实现,关键不是“看注释”,而是顺着三条主线深入源码:字段定义 → Unsafe 工具调用 → CPU 指令映射。整个过程在 JDK 25/26 中保持高度一致,且核心逻辑早在 JDK 8 就已稳定。
看清楚三个核心字段的协作关系
打开 AtomicInteger 源码(路径通常为 java.util.concurrent.atomic.AtomicInteger),先聚焦静态初始化块和字段声明:
-
private static final Unsafe unsafe = Unsafe.getUnsafe();—— 这是所有原子操作的“发动机”,它绕过 JVM 普通内存访问,直接对接操作系统和硬件 -
private static final long valueOffset;—— 它不是固定数字,而是在静态块中通过unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"))动态计算出的内存地址偏移量,确保能精确定位到value字段在对象实例中的物理位置 -
private volatile int value;—— 真实存储整数值的字段,volatile保证每次读写都直接与主内存交互,避免线程本地缓存导致的可见性问题
跟踪 compareAndSet 的调用链
这是最直接体现 CAS 语义的方法。查看其源码:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
这一行就是全部——它把当前对象、value 字段的内存地址偏移、预期旧值、目标新值四个参数,打包交给 Unsafe。这里没有循环、没有锁、没有判断分支,纯粹是一次底层指令触发。
继续跟进 Unsafe.compareAndSwapInt,你会发现它是 native 方法(JDK 25/26 中仍由 HotSpot VM 以 C++ 实现),最终映射到 CPU 的 cmpxchg 指令(x86 架构)或等效原子指令(如 ARM 的 ldxr/stxr)。这个指令由硬件保障:读取、比较、写入三步不可分割,其他线程无法插入。
理解 getAndIncrement 为何需要循环重试
对比 compareAndSet,getAndIncrement 返回的是“旧值”,所以必须确保读取和更新之间不被干扰:
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
再看 getAndAddInt 的实现(位于 Unsafe 类或其子类中):
- 先用
getIntVolatile读取当前值v - 执行
weakCompareAndSetInt(o, offset, v, v + delta)尝试更新 - 如果失败(说明已有其他线程改过
v),就重新读取再试,直到成功
这种“读—算—试—再读”的模式,就是典型的乐观锁实践:假设冲突少,靠重试而非阻塞来解决竞争。
验证是否真走 CAS 路径
实际调试时,可借助 JVM 参数辅助确认:
- 加
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需安装 hsdis),观察 JIT 编译后是否生成cmpxchg相关汇编 - 用 JOL(Java Object Layout)工具打印对象内存布局,验证
valueOffset是否与value字段真实偏移一致 - 在多线程高并发场景下打日志或用 JFR(Java Flight Recorder)采样,确认
compareAndSet返回false的频率,间接反映 CAS 失败重试行为
不复杂但容易忽略。











