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 风险。

相关面经

12 篇

启云方

AI 应用开发工程师
一面
查看原面经 →
CAS 是什么?有什么问题?ABA 问题怎么解决?

亚信科技

后端开发
实习
查看原面经 →
CAS 乐观锁的原理是什么?存在哪些潜在问题?

中科星图维天信

后端开发
实习
查看原面经 →
什么是 CAS 乐观锁?它有什么缺陷?
追问
CAS 自旋可能会带来什么风险?怎么缓解?

水滴公司

AI全栈工程师
校招秋招一面
查看原面经 →
CAS 如果不满足条件会怎么样?
追问
CAS 有什么问题?还有其他弊端吗?
追问
你刚说 CAS 等待时间会长,又说它并发快很多,这跟它矛盾吗?

百度

Java开发
社招一面
查看原面经 →
举例Java源码中用到的CAS。CAS原理。
展开剩余 7 篇面经

顺丰

后端开发工程师
一面
查看原面经 →
cas

蚂蚁集团

面经合集
查看原面经 →
解释一下 CAS 的底层原理

广东元海

Java 后端开发工程师
实习一面
查看原面经 →
9,CAS解释一下

中科创达

后端开发工程师(AI / RAG 方向)
查看原面经 →
CAS 是什么?底层原理是什么?

去哪儿

后端开发工程师
查看原面经 →
Java中对共享变量做无锁并发处理有哪些常用方式?请说明CAS的实现逻辑?

小红书

AI Native 全栈开发工程师
日常实习一面、二面
查看原面经 →
CAS 原理、ABA、自旋问题

滴滴

后端开发工程师
实习一面
查看原面经 →
CAS 是什么意思?

收录题集

1 个来源,共1 份题集

系统设计与后端基础

阿东玩 AI · AgentGuide · 2 条原题

查看题集 →

相关题目

4 题