多线程JNI调用

Multithreaded JNI-Calls

本文关键字:调用 JNI 多线程      更新时间:2023-10-16

我了解到,每次使用JNIEnv时,都必须使用jvm->AttachCurrentThread将c线程附加到jvm。这应该非常类似于互斥锁,我在方法的开始用jvm->AttachCurrentThread锁定它,在方法的结束用jvm->DetachCurrentThread()解锁它。所以,现在我有了一种方法,可以更频繁地使用JNIEnv。我每次都要打AttachCurrentThread吗?这里有一个代码示例:

  std::unique_ptr<IState> JNIGame::createEmptyState() {
    JNIEnv* env;
    jvm->AttachCurrentThread((void**)&env, NULL);
    if(!jGame_createEmptyState)
      jGame_createEmptyState =  env->GetMethodID(jGameC, "createEmptyState", "()Ljni/JNIGames$IJNIState;");
    JNIState *state = new JNIState();
    //needed?
    jvm->AttachCurrentThread((void**)&env, NULL);
    state->jStateO = env->CallObjectMethod(jGameO, jGame_createEmptyState);
    jvm->DetachCurrentThread();
    return std::unique_ptr<IState>(state);
}

正如您所看到的,我附加了两次线程,因为如果没有第二次,代码就会崩溃。但如果它们的行为像互斥锁,那么只需要第一个。你能帮我吗?为什么我每次都需要它们?它像现在的代码一样保存吗?

不确定你所说的"像互斥体一样行动"是什么意思,但文档中说,"试图附加一个已经附加的线程是不可能的。"

您的示例附加两次,然后分离一次。第二次附加无效。

我引用的这句话来自JNI文档的"调用API"一章。"调用API"是他们对需要从C调用Java的收集方法的调用(而不是用于编写可以从Java调用的本机方法的方法)


有几点值得一提:

启动JVM的线程是隐式附加的。该线程成为JVM的"主"线程。

只要最后一个非守护进程线程退出,JVM就会关闭(无法重新启动)。如果本机线程分离,则被视为线程退出。

我的应用程序有一个专用的"主"线程,它启动JVM,通知其他线程它已经启动,然后永远休眠。当我的应用程序的其他本机线程都没有使用休眠的"主"线程时,它使我的JVM保持活力。

AttachCurrentThread允许添加您在JVM外部创建的线程,以便添加到Java虚拟机中。它不打算被称为"每次使用JNIEnv时",也绝不是"类似于互斥体"。

此外,引用文件:

尝试附加已附加的线程是不可行的。

如果您的代码崩溃,可能是因为AttachCurrentThread而不是互斥。如果方法JNIGame::createEmptyState()由多个线程调用,则必须使用真正的互斥。

通常,在与虚拟机交互时,您会连接一个线程一次,并将其连接更长的时间,而不是在每个方法之后将其分离。