Java多线程复习与巩固(四)--synchronized的实现

来源:互联网 发布:日上免税店有mac吗 编辑:程序博客网 时间:2024/05/29 06:37

小小的回顾

在上一篇文章的例子中有一个Counter类:

static class Counter {    private int c = 0;    public void increment() { c++; }    public void decrement() { c--; }    public int value() { return c; }}

为了实现线程同步我们使用了synchronized关键字,而synchronized关键字有两种用法:

  1. 同步方法:

    static class Counter {private int c = 0;public synchronized void increment() { c++; }public synchronized void decrement() { c--; }public int value() { return c; }}
  2. 同步代码块:

    static class Counter {private int c = 0;public void increment() { synchronized(this){c++;} }public void decrement() { synchronized(this){c--;} }public int value() { return c; }}

反编译代码

我们把上面三段代码反编译一下,并取出incrementdecrement两个方法的反编译代码:

  1. 未加同步

     public void increment();   descriptor: ()V   flags: ACC_PUBLIC   Code:     stack=3, locals=1, args_size=1        0: aload_0        1: dup        2: getfield      #2                  // Field c:I        5: iconst_1        6: iadd        7: putfield      #2                  // Field c:I       10: return     LineNumberTable:       line 3: 0 public void decrement();   descriptor: ()V   flags: ACC_PUBLIC   Code:     stack=3, locals=1, args_size=1        0: aload_0        1: dup        2: getfield      #2                  // Field c:I        5: iconst_1        6: isub        7: putfield      #2                  // Field c:I       10: return     LineNumberTable:       line 4: 0
  2. 同步方法

     public synchronized void increment();   descriptor: ()V   flags: ACC_PUBLIC, ACC_SYNCHRONIZED   Code:     stack=3, locals=1, args_size=1        0: aload_0        1: dup        2: getfield      #2                  // Field c:I        5: iconst_1        6: iadd        7: putfield      #2                  // Field c:I       10: return     LineNumberTable:       line 3: 0 public synchronized void decrement();   descriptor: ()V   flags: ACC_PUBLIC, ACC_SYNCHRONIZED   Code:     stack=3, locals=1, args_size=1        0: aload_0        1: dup        2: getfield      #2                  // Field c:I        5: iconst_1        6: isub        7: putfield      #2                  // Field c:I       10: return     LineNumberTable:       line 4: 0
  3. 同步代码块

     public void increment();   descriptor: ()V   flags: ACC_PUBLIC   Code:     stack=3, locals=3, args_size=1        0: aload_0        1: dup        2: astore_1        3: monitorenter        4: aload_0        5: dup        6: getfield      #2                  // Field c:I        9: iconst_1       10: iadd       11: putfield      #2                  // Field c:I       14: aload_1       15: monitorexit       16: goto          24       19: astore_2       20: aload_1       21: monitorexit       22: aload_2       23: athrow       24: return     Exception table:        from    to  target type            4    16    19   any           19    22    19   any     LineNumberTable:       line 3: 0     StackMapTable: number_of_entries = 2       frame_type = 255 /* full_frame */         offset_delta = 19         locals = [ class Counter, class java/lang/Object ]         stack = [ class java/lang/Throwable ]       frame_type = 250 /* chop */         offset_delta = 4 public void decrement();   descriptor: ()V   flags: ACC_PUBLIC   Code:     stack=3, locals=3, args_size=1        0: aload_0        1: dup        2: astore_1        3: monitorenter        4: aload_0        5: dup        6: getfield      #2                  // Field c:I        9: iconst_1       10: isub       11: putfield      #2                  // Field c:I       14: aload_1       15: monitorexit       16: goto          24       19: astore_2       20: aload_1       21: monitorexit       22: aload_2       23: athrow       24: return     Exception table:        from    to  target type            4    16    19   any           19    22    19   any     LineNumberTable:       line 4: 0     StackMapTable: number_of_entries = 2       frame_type = 255 /* full_frame */         offset_delta = 19         locals = [ class Counter, class java/lang/Object ]         stack = [ class java/lang/Throwable ]       frame_type = 250 /* chop */         offset_delta = 4

Oracle官网和维基百科有相关指令的介绍

通过看底层字节码,可以看出以下几点:

  1. 普通方法和synchronized方法在方法内部没有任何区别,仅仅是synchronized方法比普通方法多了一个ACC_SYNCHRONIZED标志位,该标志位表示访问该方法需要同步访问(synchronized access)
  2. synchronized同步代码块中由于要加载this引用,多了很多指令,而关键的两个指令是monitorentermonitorexit

Oracle官网提供的JVM规范中对monitorentermonitorexit有以下介绍:

monitorenter指令用于进入对象的monitor监视器,该指令的操作数是栈中的对象引用(objectref),这个对象引用必须是引用类型,不能使基本类型。每个对象都关联着一个monitor监视器,当且仅当monitor监视器有线程所有者(owner)才会被锁住,线程通过执行monitorenter指令尝试获取对象关联的monitor监视器的拥有权:

  • 如果一个对象(objectref)关联的monitor监视器的entry进入次数为0,该线程进入monitor监视器并将它的entry进入次数设置为1。该线程则是monitor的所有者。
  • 如果一个线程已经拥有对象(objectref)关联的monitor监视器,就让它再次进入该monitor监视器,并将entry进入次数加一。
  • 如果另一个线程已经拥有了对象(objectref)关联的monitor监视器,那么该线程将会阻塞,直到monitor监视器的entry进入次数为0时再次尝试获取所有权。

注意:

  • 如果引用对象为null,monitorenter指令将会抛出空指针异常。
  • monitorenter指令可以配合monitorexit指令来实现synchronized代码块。虽然它们提供了锁定语义,但在synchronized方法中并不会执行这两个指令,而是在调用synchronized方法时monitor进入,在方法返回时monitor退出。
  • Java的同步机制除了要实现monitorentermonitorexit这样的操作,还应该包括等待monitor监视器(Object.wait),通知等待monitor监视器的其他线程(Object.notify和Object.notifyAll)。JVM指令中不会提供这些操作的支持。

monitorexit用于退出对象的monitor监视器:

  • 执行monitorexit指令的线程必须是该monitor监视器的所有者。执行该指令时,该线程会将monitorentry进入次数减一。如果entry进入次数的结果为0,该线程将退出monitor不再是它的所有者。其他进入monitor的阻塞线程可以尝试获取该monitor监视器。

JVM规范文档说的非常清楚明白,synchronized关键字是由monitor监视器实现的。

操作系统中经常把monitor称为“管程”

阅读全文
0 0
原创粉丝点击