atomicinteger底层依赖cpu的cas指令(如x86的cmpxchg),由unsafe.compareandswapint实现;incrementandget()返回新值,getandincrement()返回旧值;cas需循环重试,配合内存屏障保证可见性与有序性。

Java 中的原子操作(如 AtomicInteger.incrementAndGet())在底层依赖 CPU 提供的比较并交换(Compare-and-Swap, CAS)指令,x86/x64 架构下最核心的就是 CMPXCHG 指令。它不是 Java 直接调用的汇编指令,而是由 JVM 在 JIT 编译时,将 Unsafe.compareAndSwapInt 等方法内联为对应的硬件指令序列,其中关键一步就是生成 CMPXCHG。
CMPXCHG 指令的基本行为
CMPXCHG 是一条原子指令,执行“若内存值等于预期值,则写入新值,同时返回原内存值”。其伪逻辑如下:
- 读取目标内存地址的当前值
oldVal - 将
oldVal与寄存器AL/AX/EAX/RAX中的“期望值”比较 - 如果相等:把“新值”写入该内存地址,并将标志位
ZF(Zero Flag)置为 1 - 如果不等:不修改内存,仅将内存实际值加载进
EAX/RAX,ZF清零
整个过程由 CPU 硬件保证原子性——不会被线程切换或其它核心的操作打断,通常通过总线锁(LOCK# 信号)或缓存一致性协议(MESI + cache lock)实现。
JVM 如何把 CAS 映射为 CMPXCHG
Java 的原子类(如 AtomicInteger)内部使用 Unsafe 类的 compareAndSwapInt 方法。这个方法是 native 的,在 HotSpot JVM 中对应 C++ 实现(如 Unsafe_CompareAndSwapInt),最终会调用平台相关的汇编 stub。以 x86_64 为例:
- JIT 编译器识别到对
Unsafe.compareAndSwapInt的频繁调用后,会将其内联为一段精简的机器码 - 这段代码包含
LOCK CMPXCHG指令(LOCK前缀确保多核间操作的原子性) - 例如:将
cas(&value, expect=5, update=6)编译为类似以下汇编片段:mov eax, 5 ; 期望值 → EAX mov edx, 6 ; 新值 → EDX lock cmpxchg dword ptr [value], edx ; 原子比较并交换 setz al ; ZF=1 → AL=1,表示成功
- 返回值(是否成功)由
ZF标志决定,JVM 将其转为 Java 的boolean
为什么需要循环重试(自旋)
CMPXCHG 本身只做一次尝试。但高并发下,多个线程可能同时读到相同旧值、同时执行 CMPXCHG,结果只有一个能成功(ZF=1),其余失败(ZF=0)。因此上层逻辑必须配合循环:
-
AtomicInteger.incrementAndGet()实际是:do { old = get(); } while (!compareAndSet(old, old + 1)); - 失败时,重新读取最新值,再试一次——这叫乐观锁+自旋策略
- 避免了传统锁的挂起/唤醒开销,但在高争用时可能浪费 CPU
内存屏障与可见性保障
单纯 CMPXCHG 只保证操作原子性,不直接解决指令重排序和缓存可见性问题。JVM 在生成 CAS 指令时,会配套插入内存屏障:
-
LOCK CMPXCHG隐含了“全屏障”(full memory barrier)语义:禁止该指令前后的读写重排序 - 确保 CAS 成功后,后续读操作能看到之前所有线程对共享变量的修改(借助 MESI 协议使其它 core 的缓存行失效)
- 这也是
volatile读写和Atomic*操作能实现 happens-before 关系的硬件基础
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











