Monitor
Monitor 被翻译为监视器或管程
每个 Java 对象都可以关联一个 Monitor 对象,Monitor 也是 class,其实例存储在堆中,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针,这就是重量级锁
工作流程:
- 开始时Monitor中Owner 为 null
- 当Thread-2 执行了 synchronized(obj) 就会将 Monitor 的所有者 Owner 设置为 Thread-2, Monitor中只能有一个Owner, obj对象的Mark Word指向Monitor, 把对象原有的 MarkWord 存入线程的锁记录中
- 在Thread-2 上锁的过程中, Thread-3, Thread-4 也执行了 synchronized 的时候, obj会再次的指向 Monitor 但是Thread-3 发现 Monitor 的 Owner 已经有人了,就会进入 EntryList 阻塞队列中,并进入Blocked 状态等待
- 等Thread -2 执行完了同步代码块中的内容 , 根据obj对象头重的Monitor地址寻找,设置Owner为空 , 把线程的锁记录中的对象头的值设置会MarkWord
- 最后会唤醒EntryList 阻塞队列中的线程来竞争锁,竞争是非公平的,是根据JDK底层的某些逻辑来决定唤醒顺序
注意:
- synchronized 必须是进入同一个对象的 Monitor 才有上述的效果
- 不加 synchronized 的对象不会关联监视器,不遵从以上规则
字节码
代码:
public static void main(String[] args) {
Object lock = new Object();
synchronized (lock) {
System.out.println("ok");
}
}
0: new #2 // new Object
3: dup
4: invokespecial #1 // invokespecial <init>:()V,非虚方法
7: astore_1 // lock引用 -> lock
8: aload_1 // lock (synchronized开始)
9: dup // 一份用来初始化,一份用来引用
10: astore_2 // lock引用 -> slot 2
11: monitorenter // 【将 lock对象 MarkWord 置为 Monitor 指针】
12: getstatic #3 // System.out
15: ldc #4 // "ok"
17: invokevirtual #5 // invokevirtual println:(Ljava/lang/String;)V
20: aload_2 // slot 2(lock引用)
21: monitorexit // 【将 lock对象 MarkWord 重置, 唤醒 EntryList】
22: goto 30
25: astore_3 // any -> slot 3
26: aload_2 // slot 2(lock引用)
27: monitorexit // 【将 lock对象 MarkWord 重置, 唤醒 EntryList】
28: aload_3
29: athrow
30: return
Exception table:
from to target type
12 22 25 any
25 28 25 any
LineNumberTable: ...
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 args [Ljava/lang/String;
8 23 1 lock Ljava/lang/Object;
说明:
- 通过异常 try-catch 机制,确保一定会被解锁
23 1 lock Ljava/lang/Object;
说明:
- 通过异常 **try-catch 机制**,确保一定会被解锁
- 方法级别的 synchronized 不会在字节码指令中有所体现