为什么有 JVM 指令 `monitorenter/monitorexit` 但没有 `wait/notifyAll`(它们是本机调用)?

Why there are JVM instructions `monitorenter/monitorexit` but no `wait/notifyAll` (they are native calls)?

当我们写synchronized(some_object){}时,我们可以看到两条JVM指令monitorenter/monitorexit作为字节码发出。

当我们写 synchronized(some_object){some_object.wait()} 时,我希望看到像 wait 这样的特殊 JVM 指令,但是 none —— 而 wait/notify 是作为本机 C 函数实现的。

为什么会出现这样的不一致(要么全部作为 JNI 要么作为 java 字节码)?是否有特定(历史)原因,或者只是个人喜好问题?

上下文:我对此很感兴趣,因为在字节码中包含所有 monitorenter/monitorexit/wait/notify 将允许 'JavaByteCode program correctness verifiers that do not handle JNI' 验证不使用 JNI 的并发 Java 程序。目前,这种假设的工具必须解决 wait/notify.

i would expect to see special JVM instructions like wait

我不会。在我看来,这将是不一致的——在源代码中,您只是调用一个方法,所以您也只是在字节码中调用一个方法是有道理的。否则,编译器必须对这些方法有特殊的了解,而目前还没有。

可以说,monitorentermonitorexit 也通过方法调用实现(例如,它们在 .NET 中)更有意义。某些方法将始终是本机的并且与 JVM 本身紧密相关——我认为这没有任何不合理之处,而且我不希望每个方法都通过单独的字节码操作来实现。但是,我对 synchronized 没有 too 有太多支持它的特殊字节码的问题,因为它是一种语言结构(如 try/catch/finally),而不仅仅是一个常规方法调用。

不需要验证程序来处理 JNI,因为 waitnotify 调用的语义已明确指定。这与专用字节码指令没有什么不同。这同样适用于热点优化器如何处理许多众所周知的方法调用,其中可能包括 waitnotify。它不一定生成代价高昂的 JNI 调用,而是生成直接执行这些低级操作的代码。以这种方式处理的方法称为 intrinsic methods (see also here or here.

有这么多,如果你试图为它们中的每一个保留一个操作码,你就不能再称它为 bytecode 了。此外,以这种方式处理哪些方法取决于实际的 JVM 实现及其运行的硬件体系结构。它也可能在版本之间发生变化,因此通过为它们定义字节码指令来将其刻在石头上是没有意义的。

您写道“目前,这种假设的工具必须解决 wait/notify”。事实上,处理这些特殊方法并不是解决方法。这就是这种审计工具必须处理的许多方法,例如 Lock and Condition 中声明的方法,这些方法具有类似的线程相关语义,但现在还有许多其他著名的并发工具必须处理。

创建 monitorentermonitorexit 指令但在 Object 上创建 waitnotify 方法的确切决定是历史性的(它可以追溯到 20 多年前几年前)。今天,如果开发人员不得不再次做出决定,这个决定可能看起来会有所不同。但我想它宁愿进入甚至 monitorentermonitorexit 特殊方法的方向,这些方法在后台调用而不是字节码指令。首先,它们不再是唯一的线程同步工具。其次,这是最近的 JVM 中添加大多数新功能的方式,最好是作为方法,即使它预计会被大多数(如果不是全部)实现所固有。