Java 当 运行 来自 JNI 时,ShutdownHook 无法加入主线程

Java ShutdownHook Unable to Join Main Thread when Run From JNI

我有一些 Java 代码来创建一个关闭钩子,以便在客户端按下 ctrl+C 时干净地退出:

private static void shutdownHandler(Thread mainThread) {
    try {
        mainThread.join(30000);
    } catch (InterruptedException e) {
    }
}

public static void main(String[] args) {
    final Thread mainThread = Thread.currentThread();
    Thread shutdownThread = new Thread(() -> shutdownHandler(mainThread));
    Runtime.getRuntime().addShutdownHook(shutdownThread);
}

当我从命令行 运行 执行此操作时,它按预期工作(主线程退出并且 returns 几乎立即进入命令提示符)。但是,如果我使用以下 C++ 代码编写一个 JNI 包装器来调用它:

JavaVMInitArgs vm_args;
// Populate vm_args

JavaVM *jvm;
JNIEnv *env;
JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&env), &vm_args);

jclass mainClass = env->FindClass("path/to/my/class");
jmethod mainMethod = env->GetStaticMethodID(mainClass, "main", "([L" STRING_CLASS ";)V");

jclass stringClass = env->FindClass(STRING_CLASS);
jobjectArray mainArgs = env->NewObjectArray(0, stringClass, NULL);

env->CallStaticVoidMethod(mainClass, mainMethod, mainArgs);
jvm->DestroyJavaVM();

然后 shutdownHandler 方法挂起,直到 30 秒超时结束,然后 returns 控制 C++ 代码并最终退出。有谁知道在从 JNI 调用启动时允许 shutdownHandler 方法加入主线程的方法吗?

在您的第一个示例中,主线程退出,然后 JVM 检测到没有剩余的非守护线程并将启动 JVM 关闭。此时加入主线程没有问题,因为它在关闭之前就已经结束了。

在您的第二个变体中,主线程,即通过 env -> CallStaticVoidMethod(…) 执行 main 方法的线程正忙于执行 jvm -> DestroyJavaVM()。由于该函数等待关闭处理程序的完成,而您的关闭处理程序等待该线程的完成,因此您遇到了死锁。

您也可以使用纯 Java 代码获得类似的行为。当您将 System.exit(0); 放在 main 方法的末尾时,让主线程启动关闭并等待其完成,您会遇到类似的死锁。

通常,您不应在关闭处理程序中执行 join 操作。这些处理程序应该尽快清理并return。

或者,如 the documentation 所说:

Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down. Attempts to use other thread-based services such as the AWT event-dispatch thread, for example, may lead to deadlocks.