可以将同步块简化为字节码级别的Try-Finally块吗?

时间:2016-01-23 13:01:25

标签: java bytecode synchronized try-finally jvm-bytecode

为类似Java的语言编写自己的编译器,我在编译synchronized blocks时遇到了问题。我提出了以下想法,将它们简化为try-finally块:

synchonized (obj) {
     statements...
}

可以替换为

Object _lock = obj
_monitorEnter(lock)
try {
    statements...
}
finally {
    _monitorExit(lock)
}

_monitorEnter_monitorExit代表MONITORENTERMONITOREXIT说明。

我是否正确地假设synchronized是如何编译的,或者我错过了什么?

修改

我之前的实现对正文中的returnthrow语句进行了一些特殊处理。基本上,它会在每个lockMONITOREXIT指令之前手动加载所有*RETURN个变量和THROW个变量。这是由finally块处理的,还是我还需要这些检查?

2 个答案:

答案 0 :(得分:5)

您的假设是正确的。 Java语言中的synchronized块使用monitorentermonitorexit指令实现。您可以查看JVM规范详细信息here

  

Java虚拟机中的同步由监视器实现   明确地进入和退出(通过使用monitorenter和   monitorexit指令)或隐式(通过方法调用和   返回说明)。

编译器生成的字节码将处理synchronized正文中抛出的所有异常,所以你的try-finally方法在这里工作正常。

finally语句的

Specification没有说明释放监视器的任何信息。第一个链接中提供的示例显示了synchronized块中包含的简单方法的字节码。如您所见,处理任何可能的异常以确保monitorexit指令执行。您应该在编译器中实现相同的行为(编写将在finally语句中释放监视器的代码)。

void onlyMe(Foo f) {
    synchronized(f) {
        doSomething();
    }
}

Method void onlyMe(Foo)
0   aload_1             // Push f
1   dup                 // Duplicate it on the stack
2   astore_2            // Store duplicate in local variable 2
3   monitorenter        // Enter the monitor associated with f
4   aload_0             // Holding the monitor, pass this and...
5   invokevirtual #5    // ...call Example.doSomething()V
8   aload_2             // Push local variable 2 (f)
9   monitorexit         // Exit the monitor associated with f
10  goto 18             // Complete the method normally
13  astore_3            // In case of any throw, end up here
14  aload_2             // Push local variable 2 (f)
15  monitorexit         // Be sure to exit the monitor!
16  aload_3             // Push thrown value...
17  athrow              // ...and rethrow value to the invoker
18  return              // Return in the normal case
Exception table:
From    To      Target      Type
4       10      13          any
13      16      13          any

答案 1 :(得分:0)

正如您猜测的那样,Java编译器将同步块编译为类似于try-finally的内容。但是,有一个小的区别 - 异常处理捕获monitorexit抛出的异常并且无限地尝试释放锁。在Java中没有办法像这样指定控制流。