美文网首页
深入理解synchronize

深入理解synchronize

作者: 吃海苔的我 | 来源:发表于2018-08-22 16:42 被阅读0次

    本文参考引用,本人整理个人理解。地址点击

    1.实现原理

    synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。

    下面是一些同步的基础

    1. 普通同步方法,锁是当前实例对象;
    2. 静态同步方法,锁是当前类的class对象;
    3. 同步方法块,锁是括号里面的对象

    下面来看看一些代码进行分析

    public class SynchronizeTest {
    
        public synchronized void method1() {
            System.out.println(" synchronized method1 ");
        }
    
        public void method2() {
            synchronized (this) {
                System.out.println(" synchronized block ");
            }
        }
    
    }
    

    javap解析class文件后如下

     public synchronized void method1();
        Code:
           0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: ldc           #3                  // String  synchronized method1
           5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
           8: return
    
      public void method2();
        Code:
           0: aload_0
           1: dup
           2: astore_1
           3: monitorenter // 获取锁
           4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
           7: ldc           #5                  // String  synchronized block
           9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          12: aload_1
          13: monitorexit   // 释放锁
          14: goto          22
          17: astore_2
          18: aload_1
          19: monitorexit  // 异常情况等也需要释放锁
          20: aload_2
          21: athrow
          22: return
    

    上面可以看出method2的code的3和13分别对应synchronize监视器的进入获取锁和退出释放锁。
    说明:同步代码块是使用monitorenter和monitorexit指令实现的,同步方法(在这看不出来需要看JVM底层实现)依靠的是方法修饰符上的ACCSYNCHRONIZED实现。

    • 同步代码块

      monitorenter指令插入到同步代码块的开始位置,monitorexit指令插入到同步代码块的结束位置,JVM需要保证每一个monitorenter都有一个monitorexit与之相对应。任何对象都有一个monitor与之相关联,当且一个monitor被持有之后,他将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获取对象的锁;

    • 同步方法

      synchronized方法则会被翻译成普通的方法调用和返回指令如:invokevirtual、areturn指令,在VM字节码层面并没有任何特别的指令来实现被synchronized修饰的方法,而是在Class文件的方法表中将该方法的accessflags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的Class在JVM的内部对象表示Klass做为锁对象。
      (摘自:http://www.cnblogs.com/javaminer/p/3889023.html)

    2.对象头

    HotSpot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。其中对象头包括两部分:Mark Word 和 类型指针。

    Mark Word

    用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。Java对象头一般占有两个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit),另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

    类型指针

    对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。

    image

    其中锁标示位就是我们需要关注的

    3、Monitor

    什么是Monitor?

    
    我们可以把它理解为一个同步工具,也可以描述为一种同步机制,它通常被描述为一个对象。 
    
    与一切皆对象一样,所有的Java对象是天生的Monitor,每一个Java对象都有成为Monitor的潜质,因为在Java的设计中 ,每一个Java对象自打娘胎里出来就带了一把看不见的锁,它叫做内部锁或者Monitor锁。 
    
    Monitor 是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联(对象头的MarkWord中的LockWord指向monitor的起始地址),同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。
    
    

    Monitor 是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联(对象头的MarkWord中的LockWord指向monitor的起始地址),同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。

    其结构如下:


    image
    • Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL。

    • EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。

    • RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。

    • Nest:用来实现重入锁的计数。HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。

    • Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。
      Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。

    摘自:Java中synchronized的实现原理与应用

    4.锁优化

    java se 1.6以后对synchronize进行了优化。使其看起来没那么重
    

    相关文章

      网友评论

          本文标题:深入理解synchronize

          本文链接:https://www.haomeiwen.com/subject/fpyyiftx.html