• 线程基础知识06 synchronized使用javap查看相关指令


    1 示例-简单同步代码块

    public class SychTest9 {
    
        public static void main(String[] args) {
            Object o = new Object();
            synchronized (o){
                System.out.println("aaa");
            }
        }
    }

    使用javap查看指令(复制了部分)

    public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=4, args_size=1
             0: new           #2                  // class java/lang/Object
             3: dup
             4: invokespecial #1                  // Method java/lang/Object."<init>":()V
             7: astore_1
             8: aload_1
             9: dup
            10: astore_2
            11: monitorenter
            12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            15: ldc           #4                  // String aaa
            17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            20: aload_2
            21: monitorexit
            22: goto          30
            25: astore_3
            26: aload_2
            27: monitorexit
            28: aload_3
            29: athrow
            30: return

    可以看到下面三个指令,一个monitorenter 指令,两个monitorexit

    11: monitorenter   进入管程
    
    21: monitorexit  出管程
    
    27: monitorexit  出管程

      synchronized 同步语句块的实现使用的是 monitorentermonitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位置。**

      当执行 monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权

      在 Java 虚拟机(HotSpot)中,Monitor 是基于 C++实现的,由ObjectMonitor实现的。每个对象中都内置了一个 ObjectMonitor对象。

      另外,wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

      在执行monitorenter时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将锁计数器设为 1 也就是加 1。

      在执行 monitorexit 指令后,将锁计数器设为 0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止

      这里为什么有两个monitorexit指令呢?

      第一个monitorexit指令是同步代码块正常释放锁的一个标志;

      如果同步代码块中出现Exception或者Error,则会调用第二个monitorexit指令来保证释放锁

    2 示例-简单同步代码块+异常

    在同步代码块中手动抛出异常

    public class SychTest10 {
    
        public static void main(String[] args) throws Exception {
            Object o = new Object();
            synchronized (o){
                System.out.println("aaa");
                throw new Exception();
            }
        }
    }

    使用javap查看指令(复制了部分)

     /*
      public static void main(java.lang.String[]) throws java.lang.Exception;
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=4, args_size=1
             0: new           #2                  // class java/lang/Object
             3: dup
             4: invokespecial #1                  // Method java/lang/Object."<init>":()V
             7: astore_1
             8: aload_1
             9: dup
            10: astore_2
            11: monitorenter
            12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            15: ldc           #4                  // String aaa
            17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            20: new           #6                  // class java/lang/Exception
            23: dup
            24: invokespecial #7                  // Method java/lang/Exception."<init>":()V
            27: athrow
            28: astore_3
            29: aload_2
            30: monitorexit
            31: aload_3
            32: athrow
         */

    可以看到monitorentermonitorexitathrow和athrow,保证释放锁

    11: monitorenter
    
    27: athrow
    
    30: monitorexit
    
    32: athrow

    3 示例-普通同步方法

    public class SychTest11 {
    
        public synchronized  void aa()  {
            System.out.println("aaa");
        }
    
        public static void main(String[] args) {
    
        }

    使用javap查看指令(复制了部分)

     public synchronized void aa();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_SYNCHRONIZED
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String aaa
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
    
    

    可以看到,可以没有了monitorenter和monitorexit指令,但是有一个标识ACC_SYNCHRONIZED,标识它是一个同步方法

    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    
    

    4 示例 静态同步方法

    public class SychTest12 {
    
        public static synchronized  void aa()  {
            System.out.println("aaa");
        }
    
        public static void main(String[] args) {
            aa();
        }
    }

    使用javap查看指令(复制了部分)

     flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
        Code:
          stack=2, locals=0, args_size=0
             0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String aaa
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 6: 0
            line 7: 8
    可以看到,可以没有了monitorenter和monitorexit指令,但是有一个标识ACC_SYNCHRONIZED,标识它是一个同步方法,且ACC_STATIC标识是静态方法
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED

    5 小结

      synchronized 同步语句块的实现使用的是 monitorentermonitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。正常情况下回有两个monitorexit指令,为了在出现异常也能够成功释放锁。

      synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。

      不过两者的本质都是对监视器 monitor 的获取

     
  • 相关阅读:
    [转]利用docker进行java开发小demo
    markdown简介及语法
    Thinking in Java 之classpath理解
    docker官方windows安装
    Thinking in Java笔记之类及对象的初始化
    开发工具之play framework
    图解phpstorm常用快捷键
    Git相关
    Yii 1.1 cookie删不掉
    ajax跨域,这应该是最全的解决方案了
  • 原文地址:https://www.cnblogs.com/jthr/p/16052416.html
Copyright © 2020-2023  润新知