重量级锁通过jvm的objectmonitor调用操作系统互斥量(如pthread_mutex_lock)实现线程挂起与唤醒:竞争失败时线程进入blocked状态并调用系统调用休眠,释放锁时内核唤醒entrylist中一个线程重新竞争。

synchronized 本身不直接挂起或唤醒线程,它依赖 JVM 底层的 Monitor(监视器)机制,而 Monitor 在膨胀为重量级锁后,会借助操作系统的互斥量(mutex)和条件变量(condition variable)来实现线程阻塞与唤醒 —— 这个过程由 JVM 调用操作系统原语完成,Java 层不可见。
重量级锁如何触发线程挂起
当多个线程竞争同一把已膨胀为重量级锁的 synchronized 锁时:
- JVM 检测到锁对象的 Mark Word 指向一个重量级 Monitor 对象;
- 当前线程尝试通过
enter操作获取锁失败,Monitor 内部调用操作系统接口(如 Linux 的pthread_mutex_lock)尝试加锁; - 若锁已被占用,线程无法自旋成功,JVM 会将该线程状态设为 BLOCKED,并调用
pthread_cond_wait(或等价系统调用)让其在 Monitor 的等待队列上休眠; - 此时线程真正交出 CPU,进入操作系统内核态等待,不再消耗 CPU 时间片。
被挂起的线程如何被唤醒
唤醒发生在持有锁的线程退出 synchronized 同步块(或方法)时:
- JVM 执行锁释放逻辑,调用 Monitor 的
exit方法; - Monitor 检查内部的等待队列(EntryList)是否有阻塞线程;
- 若有,选择一个线程(通常 FIFO 或由 OS 调度策略决定),调用
pthread_cond_signal(或类似唤醒原语)通知其“锁可能可用”; - 被唤醒的线程从内核态返回用户态,重新尝试获取锁 —— 注意:它仍需再次竞争 mutex,并非立即获得锁。
关键细节:不是 Java 代码控制挂起/唤醒
这些行为完全由 JVM 在 HotSpot 中通过 ObjectMonitor 类实现,对开发者透明:
- 没有
wait()/notify()那样的显式 API; - 挂起和唤醒是原子、安全的,避免了竞态和虚假唤醒(由底层 pthread_cond 保证);
- 线程状态变化(RUNNABLE ↔ BLOCKED)由 JVM 和 OS 协同更新,可通过
jstack观察到 “waiting to lock”; - 重量级锁的开销主要来自用户态/内核态切换、上下文切换和系统调用,因此应尽量避免锁膨胀。
如何观察重量级锁行为
可以通过以下方式验证:
- 使用
-XX:+PrintGCDetails -XX:+UnlockDiagnosticVMOptions -XX:+PrintJNIGCStalls等参数辅助分析(较间接); - 更直接的是用
jstack <pid></pid>查看线程堆栈,看到类似:
- waiting to lock (a java.lang.Object) - 配合 JFR(Java Flight Recorder)录制锁事件,筛选
jdk.JavaMonitorEnter和jdk.JavaMonitorWait; - 通过
Unsafe.park(false)和Unsafe.unpark()可对比理解底层挂起/唤醒机制 —— synchronized 的重量级路径本质是封装了类似逻辑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











