重量级锁是synchronized在竞争激烈时升级的最终形态,依赖jvm创建的c++对象objectmonitor;对象头mark word后两位为10,存储指向该monitor的指针,通过_owner、_entrylist等字段实现阻塞/唤醒及可重入控制。
java里的重量级锁不是独立存在的锁类型,而是synchronized在竞争激烈、自旋失败后触发的最终锁形态,其底层实现完全依赖于一个c++对象——objectmonitor。此时,对象头(mark word)的后两位变为10,原62位内容被替换为一个指向堆中该对象专属objectmonitor结构体的指针。
重量级锁的本质就是ObjectMonitor指针
每个Java对象在首次被synchronized以重量级方式加锁时,JVM会懒加载地为其创建一个ObjectMonitor实例(并非对象一创建就存在)。这个Monitor是跨线程共享的,由JVM在堆外(C++堆)分配,其地址被写入对象头。后续所有对该对象的重量级同步操作,都通过这个指针找到并操作同一个Monitor结构。
- 对象头中存储的是指针值,不是Monitor本身;Monitor本身包含_owner、_EntryList、_WaitSet等字段
- 该指针是62位(在64位JVM开启压缩指针时可能经偏移计算),与GC标记状态(01)、偏向锁(01)、轻量级锁(00)共用同一块Mark Word空间
- 一旦进入重量级锁状态,所有线程争抢都必须走Monitor的阻塞/唤醒路径,不再尝试CAS或自旋
ObjectMonitor指针如何参与加锁与等待
当线程执行synchronized(obj)且obj已处于重量级锁状态时,JVM会通过对象头拿到ObjectMonitor指针,然后对Monitor结构进行原子操作:
- 检查
_owner == null:若为空,用CAS将当前线程设为_owner,成功则获得锁 - 检查
_owner == 当前线程:说明是重入,直接递增_recursions - 否则,线程被封装为ObjectWaiter节点,插入
_cxq(竞争队列)头部;后续由Monitor内部逻辑将其迁移至_EntryList等待唤醒 - 调用
wait()的线程会被移出_EntryList,加入_WaitSet,释放锁并挂起
为什么说“重”在指针+内核态切换
“重量级”的“重”,不单指结构复杂,更关键在于两点耦合:
- 指针间接性:每次加锁/解锁都要先解引用对象头→读取Monitor地址→再访问Monitor字段,比轻量级锁直接操作栈上Lock Record多至少一次内存寻址
- 操作系统介入:线程阻塞(park)和唤醒(unpark)需调用pthread_mutex等系统API,引发用户态到内核态切换,开销远高于纯用户态自旋
- Monitor内部的队列管理(_cxq/_EntryList/_WaitSet)涉及链表操作、条件变量、信号量等,逻辑层级深,不可忽略调度延迟
常见误区澄清
不要误以为ObjectMonitor是Java对象、可被GC回收,或可通过反射访问:
- ObjectMonitor由JVM C++代码管理,生命周期与Java对象解耦;即使Java对象被回收,Monitor可能延迟释放
- 它不继承自Object,没有Class、无方法、不可序列化,纯底层同步原语
- Java代码中无法直接获取或打印这个指针值;jstack输出的“locked ”中的地址,正是该Monitor在JVM内存中的起始地址
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











