Juc19_从字节码角度看synchronizeMonitor类monitorentermonitorexit

Posted 所得皆惊喜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Juc19_从字节码角度看synchronizeMonitor类monitorentermonitorexit相关的知识,希望对你有一定的参考价值。

①. 从字节码角度分析synchronized实现

  • ①. synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”,时间成本相对较高,这也是为什么早期的synchronized效率低的原因,后期进行了锁的优化,锁升级,对应偏向锁、轻量级锁的讲解会在下一节进行叙述
    在这里插入图片描述

  • ②. synchronized有三种应用方式
    (反编译:javap -v -p *.class > 类.txt 将进行输出到txt中)

  1. 作用于代码块,对括号里配置的对象加锁
  2. 作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁
  3. 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁
  • ③. synchronized同步代码块
  1. 实现使用的是monitorenter和monitorexit指令
    在这里插入图片描述
  2. 一定是一个enter和两个exit吗?
    (不一定,如果方法中直接抛出了异常处理,那么就是一个monitorenter和一个monitorexit)
    在这里插入图片描述
  • ④. synchronized普通同步方法
    (调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程会将先持有monitor然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放minotor)
    在这里插入图片描述
  • ⑤. synchronized静态同步方法
    (ACC_STATIC、ACC_SYNCHRONIZED访问标志区分该方法是否静态同步方法)
    在这里插入图片描述

②. 反编译synchronized锁的是什么

  • ①. 任何一个对象都可以成为一个锁,在HotSpot虚拟机中,monitor采用ObjectMonitor实现

  • ②. 上述C++源码解读

  1. ObjectMonitor.java — ObjectMonitor.cpp — ObjectMonitor.hpp
  2. ObjectMonitor.hpp(底层源码解析)
    在这里插入图片描述
  • ③. 阿里开发手册说明
    高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁

③. Monitor类

  • ①. 前言:是在Java中万物皆是对象,所以这些指令(monitorenter、 monitorexit)会不会和某些对象有关系呢? 果然可以和一个叫Monitor类联系到一块

  • ②. monitor相当于一个对象的钥匙,只有拿到此对象的monitor,才能访问该对象的同步代码。相反未获得monitor的只能阻塞来等待持有monitor的线程释放monitor。可以这样比喻吧,monitorenter 和monitorexit 对应的就是拿钥匙和还钥匙

  • ③. Monitor与java对象以及线程是如何关联 ?

  1. 首先,每一个对象都有一个属于自己的monitor,其次如果线程未获取到singal (许可),则线程阻塞。object可以比作医院的诊室,monitor 就是负责喊病人的护士,线程则是就诊的病人。
  2. 通过护士(监视器)的调度,诊室(synchronized锁住的对象)内只允许进入一个病人(线程),此病人(线程)在当前时间就拥有此诊室(对象)的使用权,也就是获取了许可。病人就诊完毕,则表明归还了诊室的使用权。然后护士再调度下一个等待的病人进入诊室(被阻塞的线程)。走廊当中等待的病人们

④. monitorenter

  • ①. 每一个对象都会和一个监视器monitor关联。监视器被占用时会被锁住,其他线程无法来获取该monitor。当JVM执行某个线程的某个方法内部的monitorenter时,它会尝试去获取当前对象对应的monitor的所有权。其过程如下:
  1. 若monior的进入数为0,线程可以进入monitor,并将monitor的进数置为1。当前线程成为monitor的owner(所有者)
  2. 若线程已拥有monitor的所有权,允许它重入monito,则进入monitor的进入数加1
  3. 若其他线程已经占有monitor的所有权,那么当前尝试获取monitor的所有权的线程会被阻塞,直到monitor的进入数变为0,才能重新尝试获取monitor的所有权
  • ②. synchronized的锁对象会关联一个monitor,这个monitor不是我们主动创建的,是JVM的线程执行到这个同步代码块,发现锁对象没有monitor就会创建monitor,monitor内部有两个重要的成员变量owner:拥有这把锁的线程,recursions会记录线程拥有锁的次数,当一个线程拥有monitor后其他线程只能等待

⑤. monitorexit

  • ①. 能执行monitorexit指令的线程一定是拥有当前对象的monitor的所有权的线程

  • ②. 执行monitorexit时会将monitor的进入数减1。当monitor的进入数减为0时,当前线程退出monitor,不再拥有monitor的所有权,此时其他被这个monitor阻塞的线程可以尝试去获取这个monitor的所有权

  • ③. monitorexit释放锁
    monitorexit插入在方法结束处和异常处,JVM保证每个monitorenter必须有对应的monitorexit

  • ④. 面试题synchroznied出现异常会释放锁吗?
    会释放锁

以上是关于Juc19_从字节码角度看synchronizeMonitor类monitorentermonitorexit的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出Java复用类从字节码角度看toString调用机制对象代理组合与继承转型final初始化

JUC并发编程 -- final关键字原理 & ASM Bytecode Outline 插件查看Java字节码 & 无状态

从jvm角度来解析java语法糖

java从字节码角度解析案例(转)

java字节码角度图解 i++ 和 ++i

从字节码角度理解JVM异常处理机制的原理