# 1.Monitor概念
- Java对象头
- 一般来说Java对象头一般占8个字节,包括4字节的Mark Word和4字节的Klass Word,对象是如何知道它具体什么类型的呢,是由Klass Word来决定的,其实Klass Word是一个指针,指向了这个对象所从属的class,比如String。。。等,简单理解就是通过这个指针找到它的类对象;
- 其中Mark Word结构为
其中Mark Word包含了很多信息,其中25位表示hashCode,这个比较简单,就是每个对象都有自己的hash码,还有4位表示在垃圾回收中的分代年龄,然后1位的biased_lock代表是不是偏向锁,最后用2位来表示加锁状态。下面几行都是代表在不同状态下MarkWord里面代表的信息,现在只大概讲一下在Normal状态下也就是正常状态下的情况。
- 数组对象
数组对象除了以上俩个还包括4个字节的数组长度
# 2.Monitor工作原理
- Monitor 被翻译为监视器或管程.每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针。简单点来说就是当一个对象获得了锁之后,他的MaikWord状态就会变成上图第五行那种情况,最后俩面变成01代表加锁成功,前30位代表指向刚刚获得的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 BLOCKED(就是进入等待队列阻塞)
- Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的
- 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲wait-notify 时会分析。
- 简单点来说,每个Java对象都会有一个Monitor锁,所有线程都是竞争这个对象的一个Monitor锁
# 2.synchronized原理
- 先来个简单例子
编译成字节码文件如下
简单描述一下这段代码:
- 从0开始 getstatic 就是获取到了synchronized(lock)lock对象的锁monitor(其实是得到了lock对象锁的引用地址),
- 3就是进行复制,
- 4 将我们获取到的对象地址复制一份出来,一会儿有用,
- 5就是开始执行synchronized里面的内容,很重要的一点就是从monitorenter开始,
- 6-11就是执行i++操作,
- 14 当操作执行完以后,获取到我们刚开始4步骤复制出来的lock对象锁的引用,通过这个引用找到lock对象锁的monitor,
- 15就是synchronized里面的内容执行完了,通过monitorexit将对象的monitor重置,为什么重置呢,因为我们最开始对象头markword存的都是hashcode,分代年龄啥的(如上面图讲的),因为我们获得了锁导致markword里面存的信息有变化,现在又要释放掉这个锁了,就要还原一下这个lock对象头里markword里面的信息(这里面有个点,虽然markword里面的信息都发生了变化,但其实它最开始的信息存在了monitor对象中,所以没有丢失),然后去唤醒monitor对象里面EntryList里面其他正在排队的线程了,
- 16就是跳到24将结果返回
- 19-24 是干什么的呢
- 刚刚我们考虑到的都是正常执行的情况,但如果出现了异常呢,如果出现了异常,19它就会把异常存储起来
- 20 还是和上面一样,获取到锁对象的引用地址,通过对象的引用地址找到monitor对象,然后进行善后工作,和上面讲的一样进行一些重置等操作。
- 22 就是拿到异常对象,然后23就是将异常对象抛出去,然后返回结果。
- 图中有一个Exception table。就是告诉我们如果程序正常执行,就步骤6-16,如果出现了异常就开始执行19-22步骤。