Monitor 和 synchronized 关键字
首先引入对象头的概念
因为 Java面向对象的思想,在jvm中需要大量的存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象的功能,这些标记字段组成了对象头
Java对象在虚拟机的组成为 Java对象 = 对象头 + 示例数据 + 对象填充
其中对象头中用于存储运行时状态的叫做 Mark Word,另一部分是元数据指针
以64为 虚拟机为例
其markword 的格式和状态如下
Monitor
- Monitor 被翻译为监视器或者管程
- 每个Java对象都可以关联一个Monitor对象,如果使用 synchronized给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向Monitor 对象的指针
- Monitor结构如下
- 刚开始是一个新的 Monitor 对象,其Owner 为 null
- 当 Thread -2 执行synchronized(Obj) 就会将 Monitor 的所有者 Owner 置为 Thread -2,Monitor中只能有一个Owner
- 在 Thread -2上锁的过程中,如果 Thread -3 Thread -4 Thread -5 也来执行synchronized(obj),就会进入 EntryList
- 而WaitSet 中存放的是 之前获得过锁,但是因为中途条件不满足 状态转为 waiting 的线程
- synchronized 必须进入同一个对象的Monitor 才有上述效果
- 不加 synchronized 的对象不会关联监视器,不遵守以上规则,也就是synchronized 和 Monitor 可以说是挂钩的(个人理解)
synchronized 原理
1. 轻量级锁
- 轻量级锁的使用场景: 如果一个对象有多线程要加索,但加锁的时间是错开的(也就是说不存在竞态),那么就可是使用轻量级锁来优化。
- 轻量级锁的对使用这是透明的,即语法仍然是 synchronized
- 假设有俩个个方法同步块利用同一个对象加锁
static final Object obj = new Object();
public static void method1() {
synchronized( obj ) {
// 同步块 Amethod2();}
}
public static void method2() {
synchronized( obj ) {
// 同步块 B}
}
- 创建所记录(lock Rocord)对象,每个线程的栈帧都会包含一个所记录的结构,内部可以存储锁定对象的Mark Word
- 让所纪律中Object reference 指向锁对象,并尝试使用cas 替换 Object 的 Mark Word,将Mark Word 的值存入锁记录
- 如果 cas 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下
- 如果加锁失败,有两种情况
- 如果是其他的线程已经持有了该Object 的轻量级锁,这是表示有竞争,进入所膨胀过程
- 如果是自己执行了synchronized重入,就是同一个对象重复加锁,那么在加一条 Lock Rcord,作为重入的计数
- 当退出synchronized 代码块 也就是该对象释放锁的时候,如果有取值为null的锁记录就表示有重入,则锁记录重置,表示重入数减一
- 当推出synchronized 代码块(解锁)是并且锁记录值部位null,这是使用cas 将Mark Word的值回复给对象头
- 成功: 解锁成功
- 失败,说明轻量级锁进行了锁膨胀,或已经升级为重量级锁,进入重量级锁的解锁流程
synchronized JVM 层面的解释
示例
static final Object lock = new Object();
static int counter = 0;
public static void main(String[] args) {
synchronized (lock) {
counter++;}
}
对应字节码
public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=10: getstatic #2 // <- lock引用 (synchronized开始)3: dup4: astore_1 // lock引用 -> slot 15: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针6: getstatic #3 // <- i9: iconst_1 // 准备常数 110: iadd // +111: putstatic #3 // -> i14: aload_1 // <- lock引用15: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList16: goto 2419: astore_2 // e -> slot 2 20: aload_1 // <- lock引用21: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList22: aload_2 // <- slot 2 (e)23: athrow // throw e24: returnException table:from to target type6 16 19 any19 22 19 anyLineNumberTable:line 8: 0line 9: 6line 10: 14line 11: 24LocalVariableTable:Start Length Slot Name Signature0 25 0 args [Ljava/lang/String;StackMapTable: number_of_entries = 2frame_type = 255 /* full_frame */offset_delta = 19locals = [ class "[Ljava/lang/String;", class java/lang/Object ]stack = [ class java/lang/Throwable ]frame_type = 250 /* chop */offset_delta = 4