CAS不是锁,而是依托CPU指令(如x86的CMPXCHG)实现的无锁原子操作,本质是乐观的“比较并交换”,依赖Unsafe类暴露、适用于高读低写场景,但存在ABA问题、自旋开销和单变量限制。

CAS 不是拿来背概念的,而是要从底层逻辑、典型陷阱和真实场景里一层层吃透。学得对,它就是高并发性能的关键杠杆;学偏了,容易写出看似线程安全、实则隐患重重的代码。
紧扣硬件与JVM底层理解CAS本质
CAS不是Java语法糖,它是CPU原生指令(如x86的cmpxchg),由JVM通过Unsafe类暴露给Java层。只看AtomicInteger的incrementAndGet()方法,等于只看到冰山一角。
- 用
javap -c反编译AtomicInteger源码,观察其调用Unsafe.compareAndSwapInt的字节码路径 - 查阅OpenJDK源码中
Unsafe::compareAndSwapInt的本地实现,确认它最终映射到哪条CPU指令 - 在HotSpot调试环境下单步跟踪CAS失败时的返回值与重试逻辑(比如
getAndIncrement内部的for循环)
亲手复现并突破三大经典缺陷
面试常问“CAS有什么问题”,但真正能动手验证并修复的开发者极少。必须自己构造场景,才能建立肌肉记忆。
-
ABA问题:用两个线程交替将AtomicInteger从100→200→100,第三个线程执行
compareAndSet(100, 300)——它会成功,但业务语义可能已错。改用AtomicStampedReference加版本戳重试 -
自旋耗尽CPU:模拟100个线程争抢同一AtomicInteger,观察
top中CPU使用率飙升。引入退避策略(如Thread.onSpinWait()或指数级sleep)缓解 -
只能保证单变量原子性:尝试用多个AtomicInteger模拟银行转账(A减、B增),发现无法保证整体事务性。此时必须升级为
StampedLock或显式锁
穿透JUC源码看CAS的真实战场
JDK并发包里,CAS不是点缀,而是骨架。不读源码,永远不知道它被用得多巧妙、多克制。
立即学习“Java免费学习笔记(深入)”;
- 细读
ConcurrentHashMap的putVal:如何用CAS初始化table、用CAS设置bin头结点、再退化为synchronized锁链表/红黑树 - 分析
LinkedBlockingQueue的offer和poll:为什么入队用CAS更新tail,出队却用ReentrantLock?——因为出队需同时修改head和next,CAS无法覆盖 - 跟踪
CompletableFuture的状态机:所有状态变更(UNCOMPLETED → COMPLETING → COMPLETE)全部基于CAS+volatile,没有一行synchronized
在压测中校准CAS的适用边界
CAS高效的前提是“低冲突”。一旦竞争激烈,它的优势迅速反转。必须用数据说话。
- 用JMH写基准测试:对比
synchronized、ReentrantLock和AtomicInteger在10/100/1000线程下的吞吐量与延迟 - 在Arthas中动态监控
AtomicInteger::incrementAndGet的CAS失败次数(通过watch命令捕获返回false的调用栈) - 观察GC日志:高频率CAS重试会引发频繁的内存屏障刷新,间接增加young GC压力——这点常被忽略


















