多线程 JNI 调用

Posted

技术标签:

【中文标题】多线程 JNI 调用【英文标题】:Multithreaded JNI-Calls 【发布时间】:2014-10-21 11:31:52 【问题描述】:

我了解到,每次使用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);

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

【问题讨论】:

如果你想从 Java 调用 C/C++ 代码,你应该考虑使用一些自动工具,比如 SWIG。 我有一个 c++ 框架,必须向 java 添加一个接口,所以新的实现可以在 java 中并使用旧框架。旧框架使用线程,但不允许我在框架内更改任何内容。 这就是 SWIG 的用途。它简化了现有 C 和 C++ 代码的绑定创建以从 Java 中使用。您(大多数情况下)不需要关心低级 JVM JNI 的东西。 swig.org @vz0 还有其他恕我直言更易于使用的工具,例如JavaCPP :) 我需要它。从 C++ 端调用 Java 方法。但是昨天我已经完成了:) 【参考方案1】:

不确定您所说的“像互斥锁一样”是什么意思,但文档说:“尝试附加已经附加的线程是无操作的。”

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

我引用的那句话来自 JNI 文档的“调用 API”一章。 “调用 API”是他们调用的收集方法,您需要从 C 调用 Java(与您用来编写可以 Java 调用的本机方法的方法相反。)


有几点值得一提:

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

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

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

【讨论】:

If a native thread detaches, that is counted as a thread exit. 非常感谢。我删除了所有分离并将所有附加更改为守护程序,它现在按计划运行。 That thread becomes the JVM's "main" thread。你在这里的“主要”是什么?您的意思是当前线程成为 JVM 内部主线程(被 JVM 用于自己的东西)或者您的意思是它实际上成为 Java 执行的主线程(调用 public static void main 的那个)?谢谢 @AndreLiberty,我很抱歉,但这个答案已经超过六年了。自从我上次接触调用调用 API 的代码以来,已经差不多有那么长的时间了。我不记得我为什么这么说。不过,这听起来像是一个简单的测试:调用一个 Java 程序,其 main() 在创建一个永久存在的新的非守护线程后返回。然后,只需注意启动 JVM 的 C 函数调用是否返回就很简单了。 很抱歉,我没有看帖子日期。并感谢您的回复【参考方案2】:

AttachCurrentThread 允许将您在 JVM 之外创建的线程添加到 Java 虚拟机中。它不打算被称为“每次使用JNIEnv”,也绝不是“类似于互斥锁”。

进一步,引用the documentation:

尝试附加已附加的线程是无操作的。

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

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

【讨论】:

以上是关于多线程 JNI 调用的主要内容,如果未能解决你的问题,请参考以下文章

JNI 通过多线程从 C++ 调用 Java

如何在JNI中使用线程

jni回调多线程问题

在java层用线程调用native方法 jni能回调主线程的方法吗?

Java多线程:CAS

Pjsip 在多线程中调用它的函数时在 android 上崩溃