CAS 的原理是什么?
未标记
考查重点:比较交换与重试代价。CAS 比预期值相等才原子写,失败重读重试,注意 ABA 和自旋成本
本题目录
参考答案
核心回答
CAS(Compare-And-Swap,比较并交换)是一种原子条件更新操作:对某个存储位置,只有当前值等于预期值时,才写入新值;不相等则更新失败。比较和写入作为一个不可分割的操作完成,不能用普通的“先判断,再赋值”代替。
在 Java 中,可以通过 AtomicInteger.compareAndSet(expected, update) 等原子类方法使用它,也可以通过 VarHandle 表达相应的原子访问语义。JVM 根据平台把操作实现为处理器支持的原子指令或等价机制,具体实现不由 Java 规范限定为某一种汇编指令。
CAS 常用于乐观并发更新:先读旧值并计算新值,再尝试提交;冲突时重新读取、重新计算,或者选择返回失败。CAS 本身不等于自旋,也不等于自旋锁,重试循环和锁的持有规则是上层算法设计。
两个线程同时加一会怎样?
假设计数器为 0,两个线程都读到 0,都想提交 0 → 1。
第一个线程成功后,计数器变为 1。第二个线程仍以 0 为预期值,CAS 就失败,不会直接覆盖第一个线程的结果;它若要完成加一,应重新读取 1,再尝试 1 → 2。
这消除了普通自增中“两个线程都读到 0,最后只写成 1”的丢失更新。仅把变量声明为 volatile,并不能把读取、计算、写入这几个步骤合成原子操作。
Java 的 AtomicInteger 已提供 incrementAndGet() 等方法,通常直接使用成熟 API。不能因为看到某版源码里有循环,就断言每个平台上的实际机器码都采用同一个循环实现。
CAS 有哪些限制?
- 竞争激烈时重试成本高:大量线程反复失败,会消耗 CPU 并争用同一缓存行。某个线程可能长期失败,
CAS不自动提供公平性;需要结合竞争程度,考虑退避、减少共享热点或使用锁等方案。 - 不能自动维护多个独立变量的不变量:账户余额和冻结金额分别做
CAS,并不能让两次更新一起成功或一起失败。可以用锁保护整个操作,或把相关状态放进不可变对象,再通过同一个原子引用整体替换;后者也要求所有更新遵守同一套协议,不能继续随意修改对象内部字段。 - 只能比较当前状态,不能直接识别修改历史:值经历
A → B → A后,再用 A 作预期值仍可能成功,这就是ABA问题。
ABA 为什么有时会破坏正确性?
如果业务只关心“当前数值是否为 A”,中间变过又变回来未必有害;但如果算法还假设“期间没有发生过变化”,CAS 就无法独自证明这一点。例如一个引用重新指回旧节点时,节点关联状态可能已经改变,旧线程依据早先读到的关系继续更新,就可能出错。
需要识别历史变化时,可以把引用和版本标记作为一份状态共同检查、共同更新。Java 的 AtomicStampedReference 就提供这种能力:它的 compareAndSet 接收预期引用、新引用、预期标记和新标记,只有引用身份和标记都匹配才原子更新两者。
标记需要随相关变更正确推进,并考虑复用或回绕范围。只加一个从不变化、或很快重复的版本字段,不能自动消除 ABA。读取引用与标记时,也应取得同一次状态快照,避免把两个不同时刻的值拼在一起。
常见追问
CAS 成功是否就表示整个业务线程安全? 只表示这次原子状态转换成立。其他共享对象、跨字段约束和后续外部操作,仍需要各自的正确性设计。
重试计算里可以发短信、扣外部款吗? 不应把不可重复的副作用放入可能反复执行的计算中。例如 updateAndGet 的更新函数可能因竞争被重新调用,应保持无副作用;本地 CAS 成功也不能和远程调用组成一个原子事务。
所有 CAS 方法的内存语义都一样吗? 不一样。本文以强 compareAndSet 为例;弱 CAS 还可能有伪失败,不同 Plain、Acquire、Release 等方法也有各自的可见性与顺序约定,应按所选 API 判断。
参考资料
- 国内:JavaGuide:CAS详解:原子类、比较更新和常见问题部分用于解释 CAS 的调用链、ABA 与竞争重试成本,并区分旧版本源码和平台实际实现。
- 国内:沉默王二:一文彻底搞清楚Java实现CAS的原理:预期值与新值的例子,以及版本标记和多个状态的讨论,用于说明失败后为什么要重新读取,以及原子更新的边界。
- 国内:陈皓:无锁队列的实现:借鉴开头对 CAS 原语及两种返回形式的说明,区分正文中的原子条件更新和普通判断赋值伪代码。
- 国外:Java SE 25:AtomicInteger:
compareAndSet、弱 CAS 和函数式更新方法的约定,用于核对失败语义、内存访问差异,以及重试函数应无副作用的要求。 - 国外:Java SE 25:AtomicStampedReference:引用与标记的配对读取、四个 CAS 参数和原子更新条件,用于解释带版本状态怎样识别 ABA 风险。




