cas的本质是cpu硬件级原子指令cmpxchg,通过lock前缀实现缓存锁或总线锁保障多核原子性,java中由unsafe.compareandswapint等方法映射执行。

CAS 的本质不是 Java 代码逻辑,而是 CPU 硬件层面的一条原子指令。理解它,关键不在“怎么写”,而在“怎么执行”——即这条指令如何被 CPU 执行、如何保证不可中断、如何协同缓存与总线。
核心指令:x86 上是 cmpxchg
Java 中的 compareAndSet 最终会编译为 x86 汇编中的 cmpxchg(Compare and Exchange)指令。它的典型形式是:
cmpxchg [addr], new_value
执行时,CPU 自动完成三件事(一步到位,不可分割):
- 将寄存器
EAX中的值(即“期望值 A”)与内存地址[addr]处的当前值比较; - 如果相等,就把
new_value写入该内存地址; - 无论成败,都会把内存原值加载回
EAX,并根据结果设置标志位(如 ZF=1 表示成功)。
原子性靠硬件锁保障
单核 CPU 下,cmpxchg 本身就能原子执行;但多核环境下,必须防止其他核同时修改同一缓存行。JVM 生成的指令实际是:
lock cmpxchg [addr], new_value
lock 前缀触发硬件级同步机制:
- 在较老 CPU 或跨缓存行场景,会发出总线锁,暂时独占内存总线;
- 在现代 CPU(支持 MESI 协议),更常用缓存锁:仅锁定当前 CPU 缓存中该变量所在的整个缓存行(64 字节),其他核对该行的读写会被阻塞或失效;
- 这个过程由 CPU 硬件自动完成,不依赖操作系统调度,也不受线程切换影响。
Java 层如何映射到这条指令
以 AtomicInteger.incrementAndGet() 为例,其底层调用链是:
incrementAndGet → getAndAddInt(this, valueOffset, 1) + 1 → Unsafe.getAndAddInt()
而 getAndAddInt 是一个典型的 CAS 自旋循环:
- 先用
getIntVolatile读取当前值(带 volatile 语义,确保看到最新值); - 计算新值(如 +1);
- 调用
compareAndSwapInt尝试更新; - 失败则重读、重算、重试——整个循环的“原子性”其实只靠那一次
cmpxchg保证,其余步骤都不原子。
为什么不能只靠 volatile?
volatile 能保证可见性和禁止重排序,但不保证操作的原子性。比如 i++ 的读-改-写三步,即使每步都 volatile,中间仍可能被其他线程插入修改。CAS 的价值就在于把“读+判+写”三步压缩成一条 CPU 指令——这才是真正不可拆分的最小执行单元。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











