【问题标题】:Can a Synchronized Block be simplified to a Try-Finally Block on the Bytecode Level?可以将同步块简化为字节码级别的 Try-Finally 块吗?
【发布时间】:2016-01-23 13:01:24
【问题描述】:

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

synchonized (obj) {
     statements...
}

可以替换为

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

其中_monitorEnter 和_monitorExit 代表MONITORENTER 和MONITOREXIT 指令。

我对@9​​87654331@ 是如何编译的这个假设是正确的,还是我遗漏了什么?

编辑

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

【问题讨论】:

  • 是的,这是正确的。事实上,这是java.util.concurrent.locks.Lock 的精确 语法。
  • 我不确定我是否理解你的想法。如果您是实现编译器的人,那么将synchronized 替换为try … finally 并不会简化任何事情,因为您仍然是必须实现try … finally 的人,不是吗?所以你仍然需要自己关心任何return 声明。
  • @Holger 当然,但我必须两次实现相同的东西,所以犯错的方法是两倍。目前,同步字节码生成只是 try/finally 语句的特化。
  • 所以更多的是平等对待他们,而不是用一个代替另一个。实际上,synchronized 和 finally 都是在字节码级别上没有等价物的构造,但具有相同的解决方法。通常,当尝试为 JVM 开发编译器时,强烈建议了解整个 “Compiling for the Java Virtual Machine” 章节(如果不是整个 JVM 规范)...

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


【解决方案1】:

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

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

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

Specification of finally 声明没有说明任何有关释放监视器的信息。第一个链接中提供的示例显示了包装在 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

【讨论】:

    【解决方案2】:

    如您所料,Java 编译器将同步块编译为类似于 try-finally 的东西。但是,有一个细微的区别——异常处理捕获了monitorexit 抛出的异常,并无限循环试图释放锁。在 Java 中没有办法像这样指定控制流。

    【讨论】:

    • 是的,可能在异常上无限循环是一项很棒的功能,尽管我不确定这是否真的是强制性的,只是因为它出现在规范的一个示例代码中……
    猜你喜欢
    • 2013-07-08
    • 1970-01-01
    • 2020-10-16
    • 2011-08-31
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 2013-05-21
    相关资源
    最近更新 更多