重量级锁引发大量上下文切换,是因为其依赖操作系统互斥量(如pthread_mutex),线程竞争失败时被挂起(用户态→内核态),释放锁后唤醒等待线程(内核态→用户态),每次切换耗时几微秒至几十微秒,高竞争下频繁阻塞/唤醒导致切换指数级增长。

重量级锁导致 CPU 飙高,核心原因不是锁本身“在干活”,而是它触发了频繁的用户态到内核态切换 + 线程阻塞唤醒调度。这种开销在高竞争场景下会指数级放大。
为什么重量级锁会引发大量上下文切换
当多个线程竞争同一把已升级为重量级的 synchronized 锁时:
- JVM 不再尝试自旋,而是调用操作系统互斥量(如 Linux 的
pthread_mutex)让线程进入阻塞状态 - futex_wait),这本身就要几十到上百纳秒
和轻量级锁、偏向锁的关键区别
这不是“锁更重所以更慢”的简单理解,而是执行路径的根本不同:
- 偏向锁:无同步开销,仅靠对象头中线程 ID 比对(一次 CAS 或直接读取),全程用户态
- 轻量级锁:通过 CAS 尝试获取锁,失败则短暂自旋(忙等),仍不进内核;自旋次数默认 10 次左右,可控
- 重量级锁:一旦膨胀,每次争锁都必然触发系统调用,无法绕过内核
怎么确认是重量级锁引发的 CPU 飙高
不能只看线程状态为 BLOCKED,要结合锁膨胀痕迹和调度行为验证:
- 用
jstack -l <pid></pid>查看线程堆栈,若大量线程停在java.lang.Object.wait(Native Method)或Unsafe.park(Native Method),且指向同一个 monitor,说明已膨胀 - 用
perf record -e 'syscalls:sys_enter_futex' -g -p <pid></pid>抓系统调用热点,futex 相关调用频次异常高即为强信号 - 用
vmstat 1观察cs(context switch)列,持续高于 10k/s 且与业务请求量正相关,大概率是锁驱动的调度风暴
缓解或规避重量级锁的实用办法
目标不是消灭重量级锁,而是不让它成为瓶颈:
- 减小锁粒度:把“整个订单处理”锁拆成“库存扣减锁 + 订单写入锁 + 日志记录锁”,避免长临界区
- 换读写锁:读多写少场景(如配置缓存、元数据查询),用
ReentrantReadWriteLock让并发读不互斥 - 用无锁结构:计数类场景优先选
LongAdder,集合类考虑ConcurrentHashMap或CopyOnWriteArrayList - 主动降级竞争:加锁前先用
AtomicBoolean.compareAndSet做快速路判断,避开锁入口
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











