繁体   English   中英

同步块中的意外代码

[英]Unexpected code in synchronized block

以下Java代码生成以下JVM字节码。

我很好奇为什么生成从偏移31到偏移36的代码。 JLS7或JVM7规范中没有任何内容涉及此问题。 我错过了什么吗?

即使我删除了println语句,代码(偏移31到偏移36)仍然只在较早的位置生成,因为println调用已被删除。

// Java code
    void testMonitor() {
        Boolean x = new Boolean(false);
        synchronized(x) {
            System.out.println("inside synchronized");
            System.out.println("blah");
        };
        System.out.println("done");
    }


// JVM bytecode
    Offset  Instruction       Comments (Method: testMonitor)
    0       new 42            (java.lang.Boolean)
    3       dup
    4       iconst_0
    5       invokespecial 44  (java.lang.Boolean.<init>)
    8       astore_1          (java.lang.Boolean x)
    9       aload_1           (java.lang.Boolean x)
    10      dup
    11      astore_2
    12      monitorenter
    13      getstatic 15      (java.lang.System.out)
    16      ldc 47            (inside synchronized)
    18      invokevirtual 23  (java.io.PrintStream.println)
    21      getstatic 15      (java.lang.System.out)
    24      ldc 49            (blah)
    26      invokevirtual 23  (java.io.PrintStream.println)
    29      aload_2
    30      monitorexit
    31      goto 37
    34      aload_2
    35      monitorexit
    36      athrow
    37      getstatic 15      (java.lang.System.out)
    40      ldc 51            (done)
    42      invokevirtual 23  (java.io.PrintStream.println)
    45      return

编译器在此处添加了一个不可见的try / catch块,以确保释放监视器状态(这在VM规范中有记录,请参见帖子底部)。 您可以使用javap -v验证这一点并查看异常表:

void testMonitor();
  Code:
   Stack=3, Locals=3, Args_size=1
   0:    new    #15; //class java/lang/Boolean
   3:    dup
   4:    iconst_0
   5:    invokespecial    #17; //Method java/lang/Boolean."<init>":(Z)V
   8:    astore_1
   9:    aload_1
   10:    dup
   11:    astore_2
   12:    monitorenter
   13:    getstatic    #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   16:    ldc    #26; //String inside synchronized
   18:    invokevirtual    #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   21:    getstatic    #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   24:    ldc    #34; //String blah
   26:    invokevirtual    #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   29:    aload_2
   30:    monitorexit
   31:    goto    37
   34:    aload_2
   35:    monitorexit
   36:    athrow
   37:    getstatic    #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   40:    ldc    #36; //String done
   42:    invokevirtual    #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   45:    return
  Exception table:
   from   to  target type
    13    31    34   any
    34    36    34   any

编辑:从JVM规范

通常,Java编程语言的编译器确保在执行synchronized语句主体之前执行的monitorenter指令实现的锁定操作与每当synchronized语句完成时由monitorexit指令实现的解锁操作匹配,无论是否完成是正常的还是突然的

我不知道它在JLS中的位置,但它必须说某个地方,当抛出异常时,会释放一个锁。 您可以使用Unsafe.monitorEnter / Exit执行此操作

void testMonitor() {
    Boolean x = new Boolean(false);
    theUnsafe.monitorEnter(x);
    try {
        System.out.println("inside synchronized");
        System.out.println("blah");
    } catch(Throwable t) {
        theUnsafe.monitorExit(x);
        throw t;
    };
    theUnsafe.monitorExit(x);
    System.out.println("done");
}

我相信最后你可能会遇到一个阻塞表。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM