synchronized的同步能力完全依赖JVM内部Monitor机制,每个Java对象天然关联一个Monitor实例,其核心结构包括Owner(持有锁线程)、Entry Set(阻塞等待线程队列)和Wait Set(wait挂起线程队列),通过monitorenter/monitorexit指令或ACC_SYNCHRONIZED标志触发Monitor获取与释放,并依托对象头Mark Word状态实现无锁→偏向锁→轻量级锁→重量级锁的动态升级。

synchronized 的同步能力完全依赖 JVM 内部的 Monitor(监视器)机制,不是靠操作系统互斥量或自旋逻辑硬实现的,而是每个 Java 对象天然关联一个 Monitor 实例。线程对 synchronized 代码块或方法的进入与退出,本质上就是对这个 Monitor 的获取与释放。
Monitor 是什么,它长在哪儿?
Monitor 不是 Java 类,也不是用户可创建的对象,它是 JVM 在对象头(Object Header)中隐式维护的一个同步结构。当一个对象被用作锁时,JVM 会检查其对象头中的 Mark Word,根据锁状态(无锁、偏向、轻量级、重量级)决定是否需要在堆中分配或复用对应的 Monitor 实例。Monitor 内部包含三部分:
- Owner:记录当前持有锁的线程 ID
- Entry Set:等待获取锁的线程队列(阻塞态,由操作系统调度唤醒)
- Wait Set:调用 wait() 后挂起的线程队列(需 notify/notifyAll 唤醒)
同步代码块怎么触发 Monitor?
编译后,synchronized(lock) {...} 会被插入 monitorenter 和 monitorexit 两条字节码指令:
- 执行 monitorenter 时,线程尝试获取 lock 对象关联的 Monitor;若 Monitor 未被占用,直接获得并把锁计数器设为 1;若已被占用,线程进入 Entry Set 阻塞等待
- monitorexit 会递减锁计数器;计数器归零时,Monitor 才真正释放,Entry Set 中的一个线程才能被唤醒竞争
- JVM 强制保证:每个 monitorenter 必有对应 monitorexit,包括异常路径——所以即使抛出 RuntimeException,锁也会被正确释放
同步方法为什么没有 monitorenter 指令?
同步方法不靠字节码指令插入,而是在方法的 access_flags 中标记 ACC_SYNCHRONIZED 标志位。JVM 方法调用流程中,invokevirtual 或 invokespecial 指令执行前会检查该标志:
立即学习“Java免费学习笔记(深入)”;
- 若存在 ACC_SYNCHRONIZED,JVM 在真正执行方法体前,自动执行一次“隐式 monitorenter”(锁对象是 this 或 Class)
- 方法正常返回或异常退出时,自动执行“隐式 monitorexit”
- 实例方法锁的是当前对象(this),静态方法锁的是当前类的 Class 对象——因为 Class 对象在 JVM 中全局唯一,天然适合作为类级别锁
锁升级是怎么配合 Monitor 工作的?
Monitor 本身不区分锁级别,但 JVM 通过对象头 Mark Word 的状态字段动态切换底层实现,目标是避免过早进入重量级锁(即真正挂起线程):
- 偏向锁:无竞争时,Mark Word 记录偏向线程 ID,后续同一线程重入无需 CAS,开销接近零
- 轻量级锁:有轻微竞争时,线程在栈帧中开辟 Lock Record,用 CAS 尝试将对象头指向它;失败则升级
- 重量级锁:CAS 多次失败后,JVM 分配 Monitor 实例,将 Mark Word 指向它,并把竞争线程放入 Entry Set 等待 OS 调度
整个过程对用户透明,Monitor 始终是统一的语义载体,只是底层资源消耗逐级上升。


















