CAS原子性由CPU硬件指令保障:x86用cmpxchg、ARM用ldrex/strex,通过缓存锁或总线锁确保“读-比较-写”不可分割;Java通过Unsafe调用底层指令,不依赖OS介入或线程阻塞。

CAS 操作在无锁设计中能保证原子性,靠的不是 Java 代码逻辑,而是 CPU 硬件指令的直接支持。它把“读取—比较—写入”这三步压缩成一条不可分割的 CPU 指令,在执行过程中不会被中断,也无需操作系统介入或线程调度参与。
硬件指令是原子性的根本
CAS 的原子性不是软件模拟出来的,而是由 CPU 提供的专用原子指令保障的:
- x86 架构用 cmpxchg 指令:一次完成“比较寄存器值与内存值,相等则写入新值”,整个过程由 CPU 硬件锁定缓存行或总线来确保排他性
- ARM 架构用 ldrex/strex 指令对:先独占加载(ldrex),再条件存储(strex),失败时返回状态标志,由软件决定是否重试
- 这些指令执行时,CPU 会通过缓存锁定(主流方式)或总线锁定(早期/特殊场景)阻止其他核心同时修改同一缓存行,从根源上杜绝撕裂操作
Java 层如何衔接硬件能力
Java 本身没有 CAS 关键字,而是通过底层机制把开发者调用映射到硬件:
- Unsafe 类提供 compareAndSwapInt 等 native 方法,内部调用 C++ 内联汇编,最终触发对应 CPU 的原子指令
- AtomicInteger 等原子类封装 Unsafe 调用,比如 incrementAndGet() 本质是循环执行 CAS,直到成功为止
- 整个链路不涉及内核态切换、不触发线程阻塞,所有操作都在用户态完成,避免了上下文切换开销
为什么“比较+交换”必须是一体的
如果拆成两步(先读再判断再写),多线程下必然出错:
- 线程 A 读到值为 100,准备改成 150
- 此时线程 B 抢先将值改为 200,再改回 100(ABA 场景)
- 若 A 不做原子校验,直接写 150,就覆盖了 B 的中间变更,造成逻辑丢失
- CAS 把“是否仍为 100”和“写 150”绑在一起执行,只要中间有变,整次操作就失败,迫使 A 重新读取最新值再试
原子性 ≠ 业务逻辑安全
CAS 保证的是单个变量读-改-写的不可中断,但它不等于并发逻辑自动安全:
- 不能替代 synchronized 去保护一段多行代码(比如“查余额→扣款→记日志”)
- 无法原子更新两个字段(如同时更新账户余额和最后操作时间戳),需要更高层协调(如锁、事务、或自定义复合 CAS 结构)
- 高竞争时反复自旋会空耗 CPU,需结合退避策略或降级为锁机制


















