美文网首页
synchronized底层原理(一)

synchronized底层原理(一)

作者: 放开那个BUG | 来源:发表于2018-11-25 15:37 被阅读9次

Synchronized的基本使用

Synchronized的作用主要有3个:

    1. 让线程互斥的访问同步代码
    1. 保证共享变量的修改能够及时可见
  • 3.解决重排序问题

在语法层面上来说,可以这样使用:

  • 1.修饰普通方法
  • 2.修饰静态方法
  • 3.修改代码块

这三种语法写到下面的代码如下:

public class Test {
    
    public synchronized void test(){
        
    }

    public synchronized static void test1(){
        
    }
    
    public void test2(){
        synchronized (Test.class) {
            
        }
    }
}

我们先用 javac Test.java 将文件编译成 Test.class 文件,再通过 javap -verbose Test 反编译得到字节码如下(只截取相关信息):

public synchronized void test();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 5: 0

  public static synchronized void test1();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 9: 0

  public void test2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // class Test
         2: dup
         3: astore_1
         4: monitorenter
         5: aload_1
         6: monitorexit
         7: goto          15
        10: astore_2
        11: aload_1
        12: monitorexit
        13: aload_2
        14: athrow
        15: return
      Exception table:
         from    to  target type
             5     7    10   any
            10    13    10   any

通过上诉字节码可以看出:

  • 同步方法,JVM使用 ACC_SYNCHRONIZED 标识来实现。即JVM通过在方法访问标识符(flags)中加入 ACC_SYNCHRONIZED 来实现通过功能。
  • 同步代码块,JVM使用 monitorentermonitorexit 指令来实现同步。

同步方法

The Java® Virtual Machine Specification针对同步方法的说明:

  • Method-level synchronization is performed implicitly, as part of method invocation and return. A synchronized method is distinguished in the run-time constant pool’s method_info structure by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method.

翻译如下:

  • 同步方法是隐式的。一个同步方法会在运行时常量池中的method_info结构体中存放ACC_SYNCHRONIZED标识符。当一个线程访问方法时,会去检查是否存在ACC_SYNCHRONIZED标识,如果存在,则先要获得对应的monitor锁,然后执行方法。当方法执行结束(不管是正常return还是抛出异常)都会释放对应的monitor锁。如果此时有其他线程也想要访问这个方法时,会因得不到monitor锁而阻塞。当同步方法中抛出异常且方法内没有捕获,则在向外抛出时会先释放已获得的monitor锁

同步代码块

同步代码块使用 monitorentermonitorexit 这两个指令实现同步,The Java® Virtual Machine Specification中有关于这两个指令的介绍:

  • Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

  • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
    If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
    If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

翻译如下:

  • 每个对象都会与一个monitor相关联,当某个monitor被拥有之后就会被锁住,当线程执行到monitorenter指令时,就会去尝试获得对应的monitor。步骤如下:

  • 每个monitor维护着一个记录着拥有次数的计数器。未被拥有的monitor的该计数器为0,当一个线程获得monitor(执行monitorenter)后,该计数器自增变为 1 。
    当同一个线程再次获得该monitor的时候,计数器再次自增;
    当不同线程想要获得该monitor的时候,就会被阻塞。
    当同一个线程释放 monitor(执行monitorexit指令)的时候,计数器再自减。当计数器为0的时候。monitor将被释放,其他线程便可以获得monitor。

  • The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

翻译如下:

  • 当线程执行monitorexit指令时,会去讲monitor的计数器减一,如果结果是0,则该线程将不再拥有该monitor。其他线程就可以获得该monitor了。

总结(面试专用)

1.同步方法通过 ACC_SYNCHRONIZED 关键字隐式的对方法进行加锁。当线程要执行的方法被标注上 ACC_SYNCHRONIZED 时,需要先获得锁才能执行该方法。

2.同步代码块通过monitorenter和monitorexit执行来进行加锁。当线程执行到monitorenter的时候要先获得所锁,才能执行后面的方法。当线程执行到monitorexit的时候则要释放锁。每个对象自身维护这一个被加锁次数的计数器,当计数器数字为0时表示可以被任意线程获得锁。当计数器不为0时,只有获得锁的线程才能再次获得锁。即可重入锁。

接下来,我们来将Monitor到底是什么?

相关文章

网友评论

      本文标题:synchronized底层原理(一)

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