Java中synchronized锁竞争致CPU飙升的本质是频繁线程阻塞/唤醒引发大量上下文切换和态切换;需通过jstack查BLOCKED线程是否扎堆同一monitor、验证锁是否升级为重量级、用vmstat/perf定位调度风暴,并通过缩小临界区、换并发工具、避免全局锁等优化。

Java 中 synchronized 锁竞争激烈导致 CPU 飙高,本质不是锁在“计算”,而是频繁的线程阻塞与唤醒引发大量上下文切换和内核态/用户态切换。排查关键在于定位“谁在等同一把锁”“锁是否已升级为重量级”“切换是否异常高频”。
看线程堆栈,确认 BLOCKED 线程是否扎堆在同一个 monitor
用 jstack -l
- 大量线程状态为 BLOCKED (on object monitor),且堆栈末尾都停在
synchronized方法或代码块入口(如at com.example.OrderService.processOrder(OrderService.java:42)) - 多个线程的 waiting to lock 指向同一个十六进制地址(即同一个对象监视器)
- 出现
java.lang.Object.wait(Native Method)或Unsafe.park(Native Method),且锁对象一致——这是重量级锁已膨胀的明确信号
查锁升级痕迹,判断是否已落到重量级
单纯看 BLOCKED 不够,要验证锁是否真的“变重”了:
- 对比 jstack 输出中该 monitor 的持有者线程:若它长期持锁(比如执行耗时逻辑),而其他线程持续等待,说明临界区过长,易触发升级
- 检查是否有 偏向锁撤销日志(启动 JVM 加
-XX:+PrintBiasedLockingStatistics -XX:+UnlockDiagnosticVMOptions),大量撤销意味着多线程争抢已打破单线程假设 - 用 Arthas watch 命令监控目标方法进入/退出,观察调用频率与平均耗时是否突增——高 QPS + 长耗时是锁升级温床
抓系统调用和上下文切换,验证调度风暴
CPU 飙高的物理源头常藏在内核层:
立即学习“Java免费学习笔记(深入)”;
- 运行 vmstat 1,持续观察 cs(context switch)列:稳定高于 10k/s 且随请求量线性上涨,高度提示锁驱动的调度风暴
- 用 perf record -e 'syscalls:sys_enter_futex' -g -p
录制 30 秒,再用 perf report 查看热点:若 futex_wait或futex_wake占比极高,基本锁定重量级锁问题 - 配合 top -Hp
找出高 CPU 线程 ID,转成十六进制后,在 jstack 结果中精确定位对应线程栈,确认是否卡在 park/wait
针对性优化,从根上缓解竞争
不追求消灭 synchronized,而是让锁不成为瓶颈:
- 缩小临界区:把大块 synchronized 逻辑拆开,只锁真正需要互斥的部分(例如只锁库存扣减,不锁日志打印或 HTTP 调用)
-
换更适合的并发结构:读多写少用
ReentrantReadWriteLock;计数类用LongAdder;缓存类用ConcurrentHashMap -
避免锁对象全局化:不要用
synchronized (Singleton.class)或synchronized (this)(尤其在 Spring Bean 中),优先用私有 final 对象做锁 - 必要时降级为无锁或乐观锁:比如库存扣减改用 CAS + 版本号,失败则重试,避开阻塞路径


















