美文网首页
Java中的各种锁(2)——隐式锁synchronized

Java中的各种锁(2)——隐式锁synchronized

作者: 技术的翅膀 | 来源:发表于2021-01-03 17:35 被阅读0次

    2 Java中的隐式锁

    在Java中,提供了关键字synchronized。这个关键字可以应用在不同的地方,如下面的表格所示:

    应用位置 锁存在于哪里 代码示例
    实例方法 当前类的实例对象 public synchronized void method(){......}
    静态方法 当前类对象 public static synchronized void method(){......}
    代码块 指定的实例对象 synchronized(object){......} //这里的object可以是任何的对象实例,比如this,Integer.MAX_VALUE这样的对象实例,都可以
    代码块 指定的类对象 syncrhonized(Demo.class){......}

    使用了synchronized关键字之后,就可以给相应的方法/代码块加上锁,保证在一个JVM中,同时只有一个线程能够执行这个方法/这个代码。但是我们并没有看到加锁和释放锁的操作,因此又被称为“隐式锁”。

    synchronized的这个功能是在JVM中通过使用monitor来实现的。

    2.1 sychronized对应的字节码

    我们先来看看下面的代码:

    public class DemoOnInstance {
        public int value = 0;
    
      public int addAndGet(int increment){
          synchronized (this){
              value = value + increment;
              return value;
          }
      }
    }
    

    在上面的代码中的addAndGet方法里面,我们使用了synchronized关键字标记了一个同步代码块,并且是将锁加在了当前实例this上。那么,JVM是怎么帮我们实现锁的呢。我们可以看看生成的字节码。
    我们可以在编译后的class文件上使用javap命令来查看字节码,javap -v DemoOnInstance.class。得到的结果会包含下面的片段,这个片段是addAndGet方法对应的字节码。

      public int addAndGet(int);
        descriptor: (I)I
        flags: ACC_PUBLIC
        Code:
          stack=3, locals=4, args_size=2
             0: aload_0
             1: dup
             2: astore_2
             3: monitorenter
             4: aload_0
             5: aload_0
             6: getfield      #2                  // Field value:I
             9: iload_1
            10: iadd
            11: putfield      #2                  // Field value:I
            14: aload_0
            15: getfield      #2                  // Field value:I
            18: aload_2
            19: monitorexit
            20: ireturn
            21: astore_3
            22: aload_2
            23: monitorexit
            24: aload_3
            25: athrow
          Exception table:
             from    to  target type
                 4    20    21   any
                21    24    21   any
    

    上面的字节码中,请注意标记行号为3和19的两行,分别为monitorentermonitorexit。这两行指令告诉JVM要获取Monitor和释放Monitor。 我们可以看到第23行也有monitorexit,这个是异常情况下的释放monitor的处理。因此无论代码是正常执行还是异常执行,都会执行monitorexit,保证锁会被释放。

    synchronized关键字应用在方法上的时候,情况略有不同。

    public class DemoOnInstanceMethod {
      private int value = 0;
    
      public synchronized int addAndGet(int increment){
        value = value + increment;
        return value;
      }
    }
    

    上面的代码中的addAndGet方法编译后的字节码如下:

      public synchronized int addAndGet(int);
        descriptor: (I)I
        flags: ACC_PUBLIC, ACC_SYNCHRONIZED
        Code:
          stack=3, locals=2, args_size=2
             0: aload_0
             1: aload_0
             2: getfield      #2                  // Field value:I
             5: iload_1
             6: iadd
             7: putfield      #2                  // Field value:I
            10: aload_0
            11: getfield      #2                  // Field value:I
            14: ireturn
    

    在字节码中,没有看到monitor相关的指令,但是在方法的flags中,有ACC_SYNCHRONIZED这个值。JVM在执行该方法的时候,如果有这个flag,就会获取锁,方法退出时(无论是正常退出还是异常退出)释放锁。

    2.2 有了字节码之后呢?

    synchronize关键字在不同的位置会生成不同的字节码,JVM在执行时会添加获取monitor和释放monitor的操作。 前面我们提到了,不同的代码锁定的对象是不一样的。那么这个是怎么实现的呢?

    前面也提到了,不同的代码方式,锁存在的位置是不一样的,这是因为在Java中,对应 synchronized 有两种锁:对象锁和类锁。
    对象锁:在非静态方法上使用 synchronized 关键字,或者使用 synchronized(objectInstance)这样的方式来创建同步代码块,使用的是对象锁。每个对象实例都有一个对象锁,不同的对象实例各自有各自的对象锁。对象锁是线程可重入的,因此在同一个线程中,可以在一个同步方法中调用另外一个同步方法。但是,一个线程在执行一个实例上的同步代码,其他线程如果要执行同样实例上的同步代码,无论是不是相同的代码段,都会被阻塞。如果两个线程执行的是同一个类的不同实例对象的同步代码块,则可以同时执行。

    类锁:在静态方法上使用 synchronized 关键字,或者使用 synchronized(Demo.class)这样的方法创建的同步代码块,使用的就是类锁。一个类只有一个类锁(感觉类似静态变量)。同样的,使用同一个类锁的同步代码段,同一时间只有一个能执行。

    类锁和对象锁互不干扰。

    2.3 锁的升级

    新创建的类锁或者对象锁,都是处于偏向锁的状态。在使用过程中,随着竞争的情况,会逐步升级为轻量锁状态或者重量锁状态。这一点在上一篇文章中已经讲过了,就不再赘述。

    2.4 小结

    synchronized 关键字会使用“隐式锁”,这个是由字节码和JVM共同来实现的,因此这个锁是“本地锁”,只能应用于同一个JVM中的不同进程,不能应用于不同的JVM之间。

    不同的 synchronized 的用法使用的锁不尽相同,有对象锁和类锁两种。对象锁和类锁都是可重入锁和独享锁,同时也都是非公平锁。

    相关文章

      网友评论

          本文标题:Java中的各种锁(2)——隐式锁synchronized

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