美文网首页
JNI多线程

JNI多线程

作者: taoyyyy | 来源:发表于2023-02-18 11:36 被阅读0次

    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

    相关文章

      网友评论

          本文标题:JNI多线程

          本文链接:https://www.haomeiwen.com/subject/xifbkdtx.html