CAS高并发瓶颈表现为RUNNABLE线程空转重试、CPU利用率偏低、吞吐不升、延迟飙升;需通过jstack查自旋栈、Async-Profiler看火焰图、JMC查调用频次,定位共享粒度过粗或误用原子变量等问题,并用JMH和压测验证优化效果。

Java 中 CAS 操作在高并发下容易因竞争激烈引发性能瓶颈,核心表现不是线程阻塞,而是大量线程在 RUNNABLE 状态空转重试,CPU 利用率偏低、吞吐不升、延迟飙升。排查关键在于识别“看似活跃实则无效”的自旋行为。
看线程状态与 CPU 使用率是否矛盾
当系统 QPS 上升但实际吞吐未提升、P99 延迟明显升高,而 CPU 使用率却未同步上升(甚至低于预期),就要高度怀疑 CAS 自旋空转。这是因为失败线程反复执行 compareAndSet(),消耗 CPU 周期却未推进业务逻辑。
- 用 jstack 多次抓取线程栈,观察是否有大量线程堆栈停留在
Unsafe.compareAndSwapInt、AtomicLong.compareAndSet或Random.next等方法上 - 对比压测前后 RUNNABLE 线程数:若激增但有效工作线程未增加,说明线程卡在重试循环中
定位热点方法和调用频次异常点
CAS 本身开销极小,问题往往出在“被高频调用的原子操作”或“共享变量被过度争抢”。需结合采样工具确认真实热点。
- 用 Async-Profiler 生成火焰图,重点关注
compareAndSet及其上游调用链(如AtomicInteger.incrementAndGet、ThreadLocalRandom.next)的 CPU 占比 - 用 JMC 或 VisualVM 观察方法调用次数:若
AtomicInteger.getAndIncrement()调用量是业务请求数的数十倍,说明存在不合理重试或共享粒度过粗 - 检查是否在循环内反复调用 CAS(如 while(!cas()) {}),且无退避策略,加剧竞争
查代码里是否误用共享原子变量或伪线程安全对象
很多瓶颈源于对“线程安全”的误解——原子类保证单个操作安全,但不等于多个原子操作组合也安全;更常见的是把本该隔离的对象全局共享。
立即学习“Java免费学习笔记(深入)”;
- 搜索项目中所有
static final AtomicInteger或static AtomicReference,尤其在工具类、Spring Bean 中作为计数器或状态开关 - 警惕
private static final ThreadLocalRandom R = ThreadLocalRandom.current();这类写法:它只在类加载时初始化一次,所有线程共用同一实例,完全失去 ThreadLocalRandom 的设计意义 - 检查并行流、CompletableFuture 中是否传入了外部共享的 Random/Atomic 对象,例如
list.parallelStream().map(x -> counter.incrementAndGet())
验证优化效果要控制变量压测
改完不能只看日志,必须量化验证。CAS 瓶颈解除后,应看到延迟下降、QPS 提升、CPU 利用率回归合理区间。
- 用 JMH 写微基准测试,隔离对比:
new AtomicInteger().incrementAndGet()vsThreadLocalRandom.current().nextInt()vs 共享AtomicInteger - 生产级压测保持相同并发线程数、请求路径和数据分布,重点观测 P99 延迟变化、QPS 提升倍数、GC 频次是否稳定
- 注意排除干扰:禁用无关监控埋点、关闭日志异步刷盘等可能掩盖真实 CAS 开销的操作



















