JNI多线程

JNIEnv与JavaVM

JavaVM 是虚拟机在 JNI 层的代表,一个进程只有一个 JavaVM,所有的线程共用一个 JavaVM。
JNIEnv 表示 Java 调用 native 语言的环境,是一个封装了几乎全部 JNI 方法的指针。
其中 JavaVM 是一个全局变量,一个进程只有一个 JavaVM 对象。而 JNIEnv 是一个线程拥有一个,不同线程的 JNIEnv 彼此独立。

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

struct JNINativeInterface {
    ...
    jint        (*GetVersion)(JNIEnv *);
    jclass      (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
                        jsize);
    jclass      (*FindClass)(JNIEnv*, const char*);
    ...
};
 
 
struct JNIInvokeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;
 
    jint        (*DestroyJavaVM)(JavaVM*);
    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
    jint        (*DetachCurrentThread)(JavaVM*);
    jint        (*GetEnv)(JavaVM*, void**, jint);
    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};

如何在多线程中获取JNIEnv

JNIEnv 只在创建它的线程生效,不能跨线程传递,不同线程的 JNIEnv 彼此独立。
native 环境中创建的线程,如果需要访问 JNI,必须用JavaVM调用 AttachCurrentThread 关联,并使用 DetachCurrentThread 解除链接。

static JavaVM *ms2_vm = NULL;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    ms2_vm = vm;
    return JNI_VERSION_1_4;
}

bool get_env(JNIEnv ** env) {
    int status = ms2_vm->GetEnv((void**) env, JNI_VERSION_1_4);
    if (status != JNI_OK) {
        status = ms2_vm->AttachCurrentThread(env, NULL);
        if(status != JNI_OK){
            ehome_printf("[%s]FAILED\n", __FUNCTION__);
            return false;
        }
        ehome_printf("[%s]SUCCESS\n", __FUNCTION__);
    }else{
        ehome_printf("[%s]Attach aready\n", __FUNCTION__);
    }
    
    return true;
}

void release_env(void) {
    JNIEnv *env ;
    int status = ms2_vm->GetEnv((void**)&env, JNI_VERSION_1_4);
    if (status == JNI_OK) {
        ehome_printf("[%s]getpid=%d, gettid=%d\n", __FUNCTION__, getpid(),gettid());
        ms2_vm->DetachCurrentThread();
    }else{
        ehome_printf("[%s]NEED NOT DETACH\n", __FUNCTION__);
    }
}

如何在多线程中管理创建的java对象引用

我们知道java对象的分配和回收是在java虚拟机,而native代码中的内存分配是在native memory。而jni又提供了在native代码中创建和访问java对象的方法,因此如何在native中维护这些java对象成为一个需要关注的问题。比如如何在native多线程中创建并访问同一个java对象,并在合适的时机去销毁?
全局引用Global Reference提供了解决方式,被其引用的对象不会被java虚拟机回收掉。我们可以用一个C++对象去管理这些java对象,在初始化函数中创建这个java对象,在析构函数中销毁和释放掉这些对象。

jni的引用分为局部引用、全局引用和虚引用,具体可以参考https://blog.51cto.com/u_12444109/3026155

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容