synchronized锁膨胀为重量级锁时,jvm将控制权交操作系统,依赖pthread_mutex_lock(linux下基于futex),通过线程阻塞与内核态调度实现,非轮询;触发条件包括自旋失败超阈值、线程挂起后新线程争抢、jvm检测高竞争状态。

当 synchronized 锁膨胀为重量级锁时,JVM 会将锁的控制权交给操作系统,底层依赖的是 POSIX 线程库中的 pthread_mutex_lock(在 Linux 上通常基于 futex 实现,但竞争激烈时退化为传统互斥量)。这个过程的核心是“线程阻塞 + 内核态调度”,不是靠轮询或用户态逻辑完成的。
重量级锁触发的条件
锁膨胀到重量级,通常发生在以下情况:
- 多个线程频繁竞争同一把锁,轻量级锁的自旋失败次数超过阈值(默认10次,可通过
-XX:SpinLimit调整); - 有线程在等待锁时被挂起,且后续仍有新线程尝试获取该锁;
- JVM 检测到当前锁对象已进入高竞争状态(例如,等待队列中已有线程),便不再尝试自旋,直接升级。
操作系统互斥量的实际工作方式
一旦升级为重量级锁,JVM 会创建或复用一个与之关联的 monitor 对象(本质是 C++ 层的 ObjectMonitor),其内部持有一个操作系统级的互斥量(如 pthread_mutex_t)。此时:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 线程调用
pthread_mutex_lock尝试加锁; - 若锁已被占用,线程不会空转,而是执行系统调用(如
futex_wait或sys_futex),主动让出 CPU 并进入 内核态休眠队列; - 持有锁的线程释放锁时,会触发内核唤醒机制(如
futex_wake),从等待队列中选择一个线程恢复运行; - 整个过程涉及用户态 → 内核态切换,每次切换开销约几百纳秒到几微秒,远高于自旋或 CAS 操作。
为什么这叫“重量级”
关键在于它动用了操作系统调度能力,代价体现在三方面:
- 态切换成本:用户态(RING3)需切换到内核态(RING0)才能执行 sleep/wake,CPU 需保存/恢复寄存器上下文;
- 调度延迟:被唤醒的线程不一定立刻执行,要等调度器分配时间片,存在不可控延迟;
- 资源占用:每个阻塞线程在内核中维护一个等待节点,大量线程争抢时,monitor 的等待队列和内核锁结构本身也会成为瓶颈。
实际表现与可观测性
你可以通过以下方式验证是否进入重量级锁:
- 使用
jstack查看线程堆栈,出现java.lang.Thread.State: BLOCKED (on object monitor); - 开启 JVM 参数
-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1,观察 safepoint 停顿是否因锁竞争加剧; - 在 Linux 下用
perf record -e sched:sched_stat_sleep,sched:sched_switch捕获线程休眠/切换事件,高频率说明重量级锁活跃。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










