Java并发编程-synchronized底层原理

synchronized底层原理与Monitor密切相关java

1.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 
复制代码

看完四件事❤️

若是你以为这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  1. 点赞,转发,有大家的 『点赞和评论』,才是我创造的动力。

  2. 关注公众号 『 JavaAC 』,不按期分享原创知识。

  3. 同时能够期待后续文章ing🚀

  4. .关注后扫码便可获取学习资料包

相关文章
相关标签/搜索