cas实现无锁并发依赖cpu硬件指令cmpxchg,通过缓存锁或总线锁保证原子性,java经unsafe类调用该指令并自旋重试,全程用户态执行、无上下文切换。

CAS 实现无锁并发,靠的不是 Java 语言本身,而是 CPU 硬件直接提供的原子指令。Java 只是把这层能力封装出来,让开发者能安全调用。
核心指令:x86 上的 cmpxchg
在 x86 架构中,CAS 对应的是 cmpxchg 指令。它把“读值—比对—写新值”三步压缩成一条不可中断的机器指令:
- 执行时,CPU 自动锁定目标内存地址所在的缓存行(cache line),其他核心无法同时修改它
- 如果当前值等于预期值,就写入新值,并设置标志位(ZF=0)表示成功
- 如果不相等,就不写,同时把内存里的最新值加载进寄存器,供上层判断是否重试
硬件怎么保证原子性不被干扰?
关键靠两种底层锁机制:
- 缓存锁(Cache Lock):现代主流方式。当变量已在某个 CPU 核心的 L1/L2 缓存中,且处于 MESI 协议的“独占(Exclusive)”或“已修改(Modified)”状态时,CPU 直接操作缓存行,无需总线参与
- 总线锁(Bus Lock):老式或特殊场景下使用。CPU 发出 LOCK# 信号锁住整条系统总线,阻止其他 CPU 访问内存——开销大,现在极少触发
Java 是怎么调用到这条指令的?
Java 通过 Unsafe 类(JDK 9+ 后逐步由 VarHandle 替代)封装了底层能力:
- AtomicInteger.incrementAndGet() 内部调用 unsafe.getAndAddInt()
- 该方法用 do-while 循环不断读取当前值 → 尝试 CAS → 失败则重试
- CAS 调用最终落到 native 方法,由 JVM 绑定到对应平台的 cmpxchg 或等效指令(如 ARM 的 ldaxr/stlxr)
为什么叫“无锁”,但又不是零成本?
它绕过了操作系统内核态的互斥锁(如 mutex),全程在用户态完成,没有线程挂起/唤醒、没有上下文切换:
- 不依赖 JVM 的 monitor 机制,也不靠线程调度来协调
- 失败后由调用方决定重试、放弃或改策略,属于乐观并发模型
- 自旋重试会消耗 CPU,尤其在高冲突场景下需注意退避策略











