CAS实现无锁并发依赖CPU硬件指令cmpxchg,通过缓存锁或总线锁保证原子性,Java经Unsafe类调用该指令,自旋重试避免线程阻塞,适用于读多写少场景。

CAS 能做到无锁并发,靠的不是 Java 本身,而是 CPU 硬件直接提供的原子指令。Java 只是把这层能力“暴露”出来,让开发者能安全地用。
核心是 CPU 的 cmpxchg 指令(x86 架构)
在 x86 CPU 上,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 替代)封装了底层能力:
立即学习“Java免费学习笔记(深入)”;
- AtomicInteger.incrementAndGet() 内部调用 unsafe.getAndAddInt()
- 该方法用 do-while 循环不断读取当前值 → 尝试 CAS → 失败则重试
- CAS 调用最终落到 native 方法,由 JVM 绑定到对应平台的 cmpxchg 或等效指令(如 ARM 的 ldaxr/stlxr)
为什么说它是“无锁”,但又不是完全没开销?
它绕过了操作系统内核态的互斥锁(如 mutex),全程在用户态完成,没有线程挂起/唤醒、没有上下文切换:
- 优势:高吞吐、低延迟,适合读多写少或竞争不激烈的场景
- 代价:失败时自旋重试,若竞争激烈(比如多个线程反复争抢同一变量),会空耗 CPU
- 限制:只能保障单个变量的原子更新,不能跨字段、跨对象协调一致性


















