Java精通并发-透过字节码理解synchronized关键字

在上一次https://www.cnblogs.com/webor2006/p/11428408.html中对于synchronized关键字的作用做了一个实例详解,下面再来看一下这个程序:

请问下,如果一个线程访问了同一个对象的method1()方法之后,另外一个线程能否访问同一个对角的method4的静态方法呢?答案是肯定的,因为method1的锁是锁的对象,而method4锁的是MyClass的类对象,锁是不一样的,当然就可以并行的进行访问啦,

关于synchronized其实从使用角度来说是比较容易理解的,但是要想充分理解它的底层其实并不是那么简单的,“偏向锁”、“轻量级锁”、“重量级锁”、“自旋锁”,可能提到这些东东顺间就懵逼了,这些其实都是在底层所能隐射出来的知识点,所以接下来会多底层来审视这个synchronized,下面一点点来,先看一下新的例子:

对于这个代码其实在实际代码中是非常之常见的,那提个疑问:

 其实,改成任何对象其效果都是一模一样的,比如改一下:

而为啥都用Object可能是成了一种标准了,所以实际代码中同步块中都会去锁一个Object对象,这里要注意:其实声明任何类型的对象其效果都是一模一样的,至于为啥在未来的底层探索中会来揭示的。

下面对这个简单的程序进行一下字节码的反编译来看一下synchronized在底层的表现:

xiongweideMacBook-Pro:main xiongwei$ javap -c com/javacurrency/test3/MyTest1.class
Compiled from "MyTest1.java"
public class com.javacurrency.test3.MyTest1 {
  public com.javacurrency.test3.MyTest1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class java/util/Date
       8: dup
       9: invokespecial #3                  // Method java/util/Date."<init>":()V
      12: putfield      #4                  // Field object:Ljava/util/Date;
      15: return

  public void method();
    Code:
       0: aload_0
       1: getfield      #4                  // Field object:Ljava/util/Date;
       4: dup
       5: astore_1
       6: monitorenter
       7: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: ldc           #6                  // String hello world
      12: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: aload_1
      16: monitorexit
      17: goto          25
      20: astore_2
      21: aload_1
      22: monitorexit
      23: aload_2
      24: athrow
      25: return
    Exception table:
       from    to  target type
           7    17    20   any
          20    23    20   any
}

其中如果想要看到更多的信息,比如常量池【这个在当时的JVM学习中已经详细学过了,就不过多解释了】,则可以用javap -v,如下:

xiongweideMacBook-Pro:main xiongwei$ javap -v com/javacurrency/test3/MyTest1.class
Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/java_javacurrency/build/classes/java/main/com/javacurrency/test3/MyTest1.class
  Last modified 2019-8-30; size 719 bytes
  MD5 checksum 44fd4afb0a37765559faa8ec0abb158f
  Compiled from "MyTest1.java"
public class com.javacurrency.test3.MyTest1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #9.#26         // java/lang/Object."<init>":()V
   #2 = Class              #27            // java/util/Date
   #3 = Methodref          #2.#26         // java/util/Date."<init>":()V
   #4 = Fieldref           #8.#28         // com/javacurrency/test3/MyTest1.object:Ljava/util/Date;
   #5 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = String             #31            // hello world
   #7 = Methodref          #32.#33        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #8 = Class              #34            // com/javacurrency/test3/MyTest1
   #9 = Class              #35            // java/lang/Object
  #10 = Utf8               object
  #11 = Utf8               Ljava/util/Date;
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/javacurrency/test3/MyTest1;
  #19 = Utf8               method
  #20 = Utf8               StackMapTable
  #21 = Class              #34            // com/javacurrency/test3/MyTest1
  #22 = Class              #35            // java/lang/Object
  #23 = Class              #36            // java/lang/Throwable
  #24 = Utf8               SourceFile
  #25 = Utf8               MyTest1.java
  #26 = NameAndType        #12:#13        // "<init>":()V
  #27 = Utf8               java/util/Date
  #28 = NameAndType        #10:#11        // object:Ljava/util/Date;
  #29 = Class              #37            // java/lang/System
  #30 = NameAndType        #38:#39        // out:Ljava/io/PrintStream;
  #31 = Utf8               hello world
  #32 = Class              #40            // java/io/PrintStream
  #33 = NameAndType        #41:#42        // println:(Ljava/lang/String;)V
  #34 = Utf8               com/javacurrency/test3/MyTest1
  #35 = Utf8               java/lang/Object
  #36 = Utf8               java/lang/Throwable
  #37 = Utf8               java/lang/System
  #38 = Utf8               out
  #39 = Utf8               Ljava/io/PrintStream;
  #40 = Utf8               java/io/PrintStream
  #41 = Utf8               println
  #42 = Utf8               (Ljava/lang/String;)V
{
  public com.javacurrency.test3.MyTest1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #2                  // class java/util/Date
         8: dup
         9: invokespecial #3                  // Method java/util/Date."<init>":()V
        12: putfield      #4                  // Field object:Ljava/util/Date;
        15: return
      LineNumberTable:
        line 5: 0
        line 6: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   Lcom/javacurrency/test3/MyTest1;

  public void method();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #4                  // Field object:Ljava/util/Date;
         4: dup
         5: astore_1
         6: monitorenter
         7: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #6                  // String hello world
        12: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: aload_1
        16: monitorexit
        17: goto          25
        20: astore_2
        21: aload_1
        22: monitorexit
        23: aload_2
        24: athrow
        25: return
      Exception table:
         from    to  target type
             7    17    20   any
            20    23    20   any
      LineNumberTable:
        line 9: 0
        line 10: 7
        line 11: 15
        line 12: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  this   Lcom/javacurrency/test3/MyTest1;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 20
          locals = [ class com/javacurrency/test3/MyTest1, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4
}
SourceFile: "MyTest1.java"

这里重点来看一下方法,首先是默认构造方法:

当然它不是我们要看的重点,要看的是method()方法:

关于上面的字节码的所有信息都在当时JVM的学习中学习过了,也不过多说,只看跟同步相关的东东:

因为要锁对象首先肯定得先获取对象才行,如下:

 

接着就执行同步块里面的方法了:

也就是:

当同步块执行完了,则会看到字节码中会退出锁:

但是!!!

下次再说~

原文地址:https://www.cnblogs.com/webor2006/p/11428811.html