cas机制通过“比较再交换”原子操作实现无锁计数,依赖volatile保障可见性,以自旋重试确保最终一致,适用于短临界区;aba问题在单调递增计数中通常不存在,但可复用场景需用atomicstampedreference规避。

CAS 机制在无锁计数器中不依赖锁,而是靠“比较再交换”的原子操作 + 自旋重试来达成最终一致。它本身不是自旋锁,但自旋是其典型协作模式——线程不阻塞,而是不断重试直到成功。
核心动作:CAS 提供原子更新能力
CAS 操作接收三个参数:内存地址 V、期望值 A、新值 B。仅当 V 当前值等于 A 时,才将 V 设为 B,并返回 true;否则返回 false,且不修改 V。这个“读—比—写”过程由 CPU 硬件指令(如 x86 的 cmpxchg)保证原子性,不会被中断或交错。
- Java 中通过
Unsafe.compareAndSwapInt或AtomicInteger.compareAndSet封装调用 - 例如计数器自增:先读当前值
old = count.get(),计算new = old + 1,再用compareAndSet(old, new)尝试提交 - 若期间有其他线程抢先更新了 count,old 值已失效,CAS 失败,当前线程需重新读取再试
自旋逻辑:失败后立即重试,而非挂起
无锁计数器把 CAS 失败当作常态,用循环实现自旋,确保操作终将成功:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 典型结构是 do-while 或 while 循环,例如:
do { old = count.get(); new = old + 1; } while (!count.compareAndSet(old, new)); - 自旋不释放 CPU,适合临界区极短(如一次整数加法)、竞争不激烈的场景
- 高竞争下可能空转耗电,工业实现常加入退避策略,比如
Thread.onSpinWait()或指数级延迟
volatile 保障可见性与有序性
CAS 只管原子性,但无法让其他线程“立刻看到”变更。所以所有被 CAS 修改的字段必须声明为 volatile:
-
volatile确保每次读都从主存加载最新值,每次写都立即刷回主存 - 禁止编译器和 CPU 对 volatile 读写做重排序,使 CAS 前的计算逻辑不会被移到 CAS 之后执行
- Java 的
AtomicInteger内部正是用volatile int value+ Unsafe CAS 实现的
应对 ABA 问题:计数器虽简单,但扩展需谨慎
标准计数器(只增不减、无回收)一般不面临 ABA 风险,因为值单调递增,不会出现 “A→B→A”。但若计数器支持复位、重用或带状态回收(如对象池计数),就可能出问题:
- 例如:某线程读到值为 100,准备设为 101;此时另一线程将 100→200→100(如误操作或绕过原子类),CAS 仍会成功,但语义已错
- 解决方案是升级为带版本号的原子引用,如
AtomicStampedReference,把“值+版本”作为整体校验维度 - 普通计数场景无需引入 stamp,但设计可复用的无锁组件时,应提前评估状态回绕风险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










