cas通过cpu原子指令(如cmpxchg)实现无锁自旋更新:先读当前值v,比较是否等于预期值a,相等则写入新值b,否则重试;全程用户态运行,避免线程挂起与上下文切换,故在低竞争下比synchronized更高效。

Java 中 AtomicInteger 的累加操作(如 incrementAndGet()、addAndGet())底层就是靠 CAS(Compare-And-Swap)实现的,它天然不依赖 synchronized 或 ReentrantLock,因此避免了线程阻塞和上下文切换开销,在高并发、低竞争场景下比传统加锁快得多。
CAS 是怎么替代加锁工作的?
CAS 是一种无锁算法:每次更新前先读取当前值(V),再比较当前值是否仍等于预期值(A),如果相等,就把新值(B)写入;否则重试。整个过程由 CPU 指令(如 x86 的 cmpxchg)保证原子性,无需操作系统介入调度。
AtomicInteger 的 incrementAndGet() 就是典型 CAS 循环:
- 读取当前值
current - 计算期望新值
next = current + 1 - 调用
unsafe.compareAndSwapInt(this, valueOffset, current, next) - 失败则重新读取、再算、再试,直到成功
为什么 CAS 比加锁快?
关键在“无阻塞”和“轻量级同步”:
- 加锁时,未获取锁的线程会挂起(进入 BLOCKED/WAITING 状态),唤醒需内核参与,开销大
- CAS 失败只是重试,线程始终运行在用户态,没有上下文切换
- 在冲突不频繁(比如每秒几千次更新、几十个线程)时,平均只需 1~3 次尝试就能成功
- JVM 对
Unsafe.compareAndSwap*有深度优化,常被内联为单条 CPU 指令
要注意的边界情况
CAS 不是万能加速器,这些情况会影响效果:
-
高竞争下自旋耗 CPU:上百线程同时争一个 AtomicInteger,可能反复失败、空转,反而拖慢整体性能(此时可考虑
LongAdder) - ABA 问题存在但通常无关紧要:值从 A→B→A,CAS 会误认为没变。对计数器类场景(只关心数值变化,不关心中间状态)完全无影响
-
不能替代复合逻辑:比如“先判断再更新”(if-then-update)无法靠单个 CAS 完成,需配合循环或使用
AtomicStampedReference
什么时候该用 AtomicInteger 而不是锁?
满足以下任一条件,优先选 AtomicInteger:
- 只做简单读-改-写(如计数、序列号生成、状态标志翻转)
- 更新逻辑是纯函数式(无 I/O、无锁、无副作用)
- 热点变量竞争可控(例如每个业务请求更新不同 key 的计数器,而非所有线程狂刷同一个 count)
- 需要更高吞吐、更低延迟,且能接受偶尔的自旋重试
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











