为类似Java的语言编写自己的编译器,我在编译synchronized blocks
时遇到了问题。我提出了以下想法,将它们简化为try-finally
块:
synchonized (obj) {
statements...
}
可以替换为
Object _lock = obj
_monitorEnter(lock)
try {
statements...
}
finally {
_monitorExit(lock)
}
_monitorEnter
和_monitorExit
代表MONITORENTER
和MONITOREXIT
说明。
我是否正确地假设synchronized
是如何编译的,或者我错过了什么?
修改
我之前的实现对正文中的return
和throw
语句进行了一些特殊处理。基本上,它会在每个lock
或MONITOREXIT
指令之前手动加载所有*RETURN
个变量和THROW
个变量。这是由finally
块处理的,还是我还需要这些检查?
答案 0 :(得分:5)
您的假设是正确的。 Java语言中的synchronized
块使用monitorenter
和monitorexit
指令实现。您可以查看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中没有办法像这样指定控制流。