CAS是一种无锁原子操作,通过Unsafe类和atomic包实现,具有低开销、高吞吐优势,但存在ABA问题、自旋CPU消耗、仅支持单变量原子性及内存模型语义弱于锁等局限。

CAS(Compare-And-Swap)操作在 Java 中主要通过 Unsafe 类和 java.util.concurrent.atomic 包实现,它是一种无锁(lock-free)的并发原语;相比传统 synchronized 或 ReentrantLock 等加锁机制,它在特定场景下有明显优势,但也存在固有局限。
低开销与高吞吐:避免线程阻塞和上下文切换
CAS 不会挂起线程,失败时通常自旋重试,省去了操作系统级的线程调度、锁获取/释放开销。尤其在竞争不激烈、操作极轻量(如计数器自增)时,吞吐量显著高于锁。
- 多个线程同时更新 AtomicInteger 时,大部分 CAS 成功,几乎无等待
- 而用 synchronized 包裹 i++,每次进入临界区都需获取/释放监视器,涉及 JVM 和 OS 协作,延迟更高
- 适用于短时间、高频、小粒度操作,比如信号量计数、状态标志位切换
ABA 问题:值被改回原样导致逻辑误判
CAS 只检查“当前值是否等于预期值”,不关心中间是否被修改过。若某变量从 A → B → A,CAS 会误认为未被改动而成功,但业务语义可能已破坏。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 典型例子:栈顶节点被弹出(A→null),另一线程压入新节点又弹出,使栈顶重新变为 A;此时原线程 CAS 判定“还是 A”,错误地完成后续操作
- Java 提供
AtomicStampedReference通过附加版本号(stamp)解决,但增加内存与逻辑复杂度 - 并非所有场景都需要防范 ABA,需结合业务判断是否引入 stamp 开销
自旋消耗 CPU 与单变量限制
CAS 本质是乐观策略,失败后常采用循环重试(如 getAndIncrement 内部 do-while 自旋)。在高争用场景下,可能持续占用 CPU 资源却迟迟无法成功。
立即学习“Java免费学习笔记(深入)”;
- 若多个线程反复 CAS 同一变量失败,会形成“活锁”倾向,吞吐反而低于适度阻塞的锁
- CAS 原子操作仅支持对单个变量(或对象引用)进行条件更新;无法像锁那样保护多变量、跨方法的复合操作
- 例如“先读 A 值,再根据 A 更新 B”,这种 read-modify-write 序列无法用单个 CAS 安全完成,必须借助锁或其他协调机制
内存可见性依赖 volatile,但不等价于锁的语义保障
CAS 操作本身带有 volatile 读写语义(如 Unsafe.compareAndSwapInt 对应 volatile load + CAS),能保证变量最新值对其他线程可见。但它不提供锁所具备的代码块原子性和内存屏障强度。
- synchronized 不仅保证临界区内共享变量可见,还禁止指令重排序,并建立 happens-before 关系覆盖整个同步块
- CAS 变量虽为 volatile,但其周边非 volatile 变量仍可能被重排序,需额外使用
VarHandle或显式Unsafe.storeFence()控制 - 因此,不能简单认为“用了 AtomicXXX 就不用考虑内存模型”,复杂逻辑仍需谨慎设计同步边界

















