synchronized底层原理与Monitor密切相关java
以 32 位虚拟机为例
普通对象算法
对象的类型,如Student类型,Teacher类型等是由KlassWord来表示的,它是一个指针,指向了对象所从属的Class。即找到类。
其中 Mark Word 结构为数组
其中,age表示垃圾回收时的分代年龄,具体可见分代收集算法。年龄超过必定岁数就从幸存区调入到老年代。最后两位,biased_lock和01表示偏向锁和加锁状态。其它表示在不一样状态下每一位所表明的含义。Normal状态表示对象的正常状态。对它加各类所的时候,其值一般会改变。 数组对象markdown
注意:因此,这里咱们也能够想到,在一个Integer对象里,要保存8个字节的对象头,还有4个字节的int类型的数据。oop
总共12字节,而基本数据类型int则只需4个字节。在内存敏感的状况下,建议用基本类型。 2.Monitor(锁)工做原理 synchronized底层原理能够用Monitor工做原理来解释 Monitor被翻译为监视器或者管程 每一个 Java 对象均可以关联一个 Monitor 对象,若是使用 synchronized 给对象上锁(重量级)以后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针学习
当咱们线程2执行以下代码时:this
synchronized(this){
//处理相关业务
}
复制代码
1.Thread2线程执行上述代码时,当前对象this会被上一把锁,这是一把重量级锁,this对象头的Mark Word字段指向了操做系统建立的Monitor对象引用地址 MarkWord在没有加任何锁的时候,即Normal状态,标记位为01,一旦获取了锁,就会尝试找一个monitor与之关联,而后把最后两位也即标记位从01改成10,而且把前面的全部位改为指向monitor对象的指针,占用30位。此时因为只有Thread2线程,因此成功获取了锁。理所应当的成为了monitor对象的owner。 owner表示Monitor锁的持有者,并且同一个时刻只能有一个owner spa
2.Monitor对象只能有一个owner,此时若是有其它线程如Thread-3或Thread-4等线程要获取这把锁就要进入Monitor对象的堵塞队列EntryList中等待Thread2释放锁。 EntryList能够理解位阻塞队列或等待队列。一直等待到其它线程释放了owner的全部权 操作系统
3.等待锁资源被释放后,Thread-3或Thread-4会互相竞争锁资源,并不能保证谁获取到锁,最终仍是有CPU来决定。 .net
4.Monitor对象的WaitSet存放的是,获取到锁的线程,可是因为其它一些缘由致使线程进入Waiting状态,又释放了锁资源,待介绍 一样地,对于下图
1.刚开始 Monitor 中 Owner 为 null
2.当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的全部者 Owner 置为 Thread-2,Monitor中只能有一个 Owner
3.在 Thread-2 上锁的过程当中,若是 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList BLOCKED
4.Thread-2 执行完同步代码块的内容,而后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的
5.图中 WaitSet 中的 Thread-0,Thread-1 是以前得到过锁,但条件不知足进入 WAITING 状态的线程,后面讲wait-notify 时会分析
synchronized 必须是进入同一个对象的 monitor 才有上述的效果
不加 synchronized 的对象不会关联监视器,不听从以上规则
3.字节码角度理解synchronized底层工做原理
public class synchronized1 {
static final Object lock=new Object();
static int counter=0;
public static void main(String[] args){
synchronized (lock){
counter++;
}
}
}
复制代码
反编译成字节码,具体反编译过程及字节码指令介绍能够参看JVM学习-字节码指令
字节码以下:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: getstatic #2 //<-lock引用(synchronized开始)
3: dup
4: astore_1 //lock引->slot1
5: monitorenter //将lock对象Markword置为monitor指针
6: getstatic #3 // Field counter:I
9: iconst_1
10: iadd
11: putstatic #3 // Field counter:I
14: aload_1 //<-lock引用,拿到刚开astore1刚才存储的临时变量
15: monitorexit //将lock对象MarkWord重置,唤醒EntryList.从新设置markword其它字段,让entryList中正在等待的线程竞争锁
16: goto 24
/*
若是同步代码块中发生了异常,而后就处理下列代码,即19-23行。
最下面的Exception table表第一行时监测6-16行是否发生了异常,即同步代码块中的代码。
若是发生了异常,就跳转到19行执行。
先把异常对象存储进来,而后根据对象引用地址找到monitor,而后也是作一些善后的工做。
把monitor中的状态还原,并唤醒entrylist中的其它线程
*/
19: astore_2
20: aload_1
21: monitorexit
22: aload_2
23: athrow
24: return
Exception table:
from to target type
6 16 19 any
19 22 19 any
复制代码
若是你以为这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
点赞,转发,有大家的 『点赞和评论』,才是我创造的动力。
关注公众号 『 JavaAC 』,不按期分享原创知识。
同时能够期待后续文章ing🚀
.关注后扫码便可获取学习资料包