重量级锁底层依赖操作系统互斥原语(如futex/pthread_mutex),通过jvm维护的objectmonitor实现阻塞唤醒;其“重”在于用户态/内核态切换、上下文切换及monitor内存开销。

Java 中的重量级锁(Heavyweight Lock)底层依赖于操作系统提供的互斥机制,通过 监视器锁(Monitor Lock) 实现线程间的互斥访问。它不是 JVM 自行用纯 Java 或 CAS 模拟的,而是借助了操作系统的线程同步原语(如 Linux 的 futex 或 pthread mutex),配合对象头中的 monitor 结构完成阻塞与唤醒。
监视器锁的核心载体:ObjectMonitor 对象
JVM 为每个被加锁的对象在堆外(C++ 堆)维护一个 ObjectMonitor 结构,它包含关键字段:
- _owner:指向当前持有锁的线程(或 NULL)
-
_waitSet:等待在该 monitor 上调用
wait()的线程队列 - _entryList:等待获取锁的线程队列(竞争失败后挂起在此)
- _recursions:重入计数(支持 synchronized 可重入)
- _object:关联的 Java 对象指针
当线程执行 synchronized(obj) 时,JVM 会尝试将当前线程设为 _owner;若失败,则把线程封装成 ObjectWaiter 节点加入 _entryList,并调用操作系统接口(如 pthread_mutex_lock 或 futex_wait)进入阻塞状态。
从偏向锁/轻量级锁升级到重量级锁的过程
重量级锁不是初始状态,而是在多线程激烈竞争下由 JVM 动态升级而来:
- 无竞争时:使用偏向锁(标记在对象头,不涉及 Monitor)
- 轻度竞争:升级为轻量级锁(CAS 替换对象头指向栈中 Lock Record)
- 出现自旋失败或等待线程数超阈值(如 -XX:PreBlockSpin,默认10次):触发 锁膨胀(inflate),创建并初始化
ObjectMonitor,对象头指向该 monitor 地址,后续所有同步操作都走 monitor 路径
一旦膨胀,该对象后续的所有 synchronized 操作都会走重量级路径,即使之后竞争消失也不会自动降级(JDK 10+ 引入了部分场景下的锁降级尝试,但重量级→轻量级仍受限)。
操作系统层面的阻塞与唤醒协作
ObjectMonitor 本身不直接管理线程调度,而是委托给 OS:
- 线程在
_entryList中等待时,JVM 调用os::park()→ 底层转为pthread_cond_wait或futex(FUTEX_WAIT),让线程进入内核态休眠 - 释放锁时(
monitorexit),JVM 遍历_entryList,对至少一个等待线程调用os::unpark()→ 触发futex(FUTEX_WAKE)或pthread_cond_signal,将其唤醒并重新参与锁竞争 - 这种“用户态判断 + 内核态阻塞”组合,兼顾了低竞争时的效率和高竞争时的公平性与可靠性
为什么叫“重量级”?关键开销在哪
相比 CAS 自旋或偏向锁,重量级锁的“重”体现在:
- 每次竞争失败都要进行 用户态到内核态的切换(syscall 开销大)
- 线程挂起/唤醒涉及 上下文切换(Context Switch),需保存寄存器、切换内存映射等
- Monitor 对象分配在 C++ 堆,有额外内存与生命周期管理成本
- 阻塞队列(_entryList / _waitSet)操作需要锁保护(Monitor 内部用
mutex同步),引入二级同步开销
这些代价使得重量级锁适合长临界区或必然阻塞的场景,而不适合高频、短时间的同步操作——这也是为何 JVM 设计了多层锁优化策略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











