当前位置: 代码迷 >> 综合 >> Monitor 和 synchronized 关键字
  详细解决方案

Monitor 和 synchronized 关键字

热度:85   发布时间:2023-12-14 08:43:28.0

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
  相关解决方案