zoukankan      html  css  js  c++  java
  • 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的学习中学习过了,也不过多说,只看跟同步相关的东东:

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

     

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

    也就是:

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

    但是!!!

    下次再说~

  • 相关阅读:
    应用层协议及ip地址划分
    请求与响应编码及jsp基本原理
    springboot注解
    springboot 快速入门
    Http协议简单解析及web请求过程
    Tomcat原理详解及请求过程
    mysql数据库乱码的问题解决
    AOP的实现原理
    Springl利用Aspectj的扩展实现Aop
    JDK动态代理实现原理
  • 原文地址:https://www.cnblogs.com/webor2006/p/11428811.html
Copyright © 2011-2022 走看看