synchronized代码块通过monitorenter和monitorexit指令实现锁机制,monitorenter触发Monitor所有权获取流程,包括检查Owner字段、更新count计数器或进入EntryList等待;用javap -c可查看字节码中的这两个指令及异常处理路径。

直接看字节码是最直观的方式:synchronized 代码块在编译后,一定会插入 monitorenter 和 monitorexit 指令,而 monitorenter 就是锁获取动作的起点。
monitorenter 指令到底做了什么
它不是简单“打个标记”,而是触发一整套 JVM 锁机制:
- 尝试获取括号内对象所关联的 Monitor 所有权;
- 若 Monitor 的 Owner 字段为空,或当前线程已持有该 Monitor,则将锁计数器(count)加 1,进入临界区;
- 若 Monitor 已被其他线程持有,当前线程会进入该对象的 EntryList 队列,状态变为 BLOCKED,等待被唤醒;
- 这个过程隐式依赖对象头中的 Mark Word —— 它实时记录着锁状态(如是否偏向、是否轻量级、是否指向重量级 Monitor)。
怎么用 javap 看到 monitorenter
写一个最简示例:
public class SyncTest {
private final Object lock = new Object();
public void m() {
synchronized (lock) {
System.out.println("ok");
}
}
}
编译后执行:javap -c SyncTest.class,你会在 m() 方法的字节码中看到类似结构:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 第 3 行左右出现
monitorenter(紧随aload_1或dup后); - 紧接着是业务逻辑字节码(如
getstatic,ldc); - 之后出现第一个
monitorexit(正常退出路径); - 再往后有异常处理块,包含第二个
monitorexit(确保抛异常时仍能释放锁)。
注意两个关键细节
很多初学者容易忽略这两点,但它们直接影响对锁行为的理解:
- monitorenter 不等于“立刻上锁成功”:它只是发起请求,是否获得锁取决于 Monitor 当前状态和竞争结果;
- 同一个同步块必然对应一个 monitorenter,但 monitorexit 出现两次是常态:JVM 自动插入异常出口的释放逻辑,这是 synchronized 比 ReentrantLock 更安全的底层保障。
对比同步方法:没有 monitorenter 也不代表没锁
如果把 synchronized 加在方法上,比如 public synchronized void m(){},字节码里不会出现 monitorenter,取而代之的是方法属性里的 ACC_SYNCHRONIZED 标志位。JVM 在调用该方法前,会自动完成等效于 monitorenter 的锁获取动作——只是不体现在字节码指令层面。

















