侧边栏
首页代码

JNI中AttachCurrentThread和DetachCurrentThread的问题

在《Java与CC++交互JNI编程》中有讲过AttachCurrentThreadDetachCurrentThread的使用。

我们知道在jni中我们可以使用pthread或者std::thread创建线程。因为线程并不是从Java环境创建的,所以这时候创建出的线程是没有JNIEnv的。如果需要使用JNIEnv,可以调用JavaVMAttachCurrentThread将当前线程附加到虚拟机。

jint AttachCurrentThread(JNIEnv** p_env, void* thr_args);

AttachCurrentThread可以调用多次,第一次会附加当前线程到虚拟机并返回JNIEnv,之后再调用的时候因为当前线程已经被附加到虚拟机上了,所以就不需要重复附加了,仅仅只返回JNIEnv即可,作用相当于GetEnv

需要注意的是,在线程退出之前我们必须要调用DetachCurrentThread从虚拟机分离当前线程,,不然会造成内存泄露,线程也不会退出。对于native层创建出来的线程,在调用AttachCurrentThread的时候会创建本地引用表,在调用DetachCurrentThread的时候会释放本地引用表。

但是一般我们并不会频繁的调用AttachCurrentThread/DetachCurrentThread,这样效率很低。一般我们在线程的入口函数调用一次AttachCurrentThread,在线程入口函数退出之前调用一次DetachCurrentThread即可。所以一定要手动释放每个本地引用。不然本地引用越来越多,很容易超出最大限制。
下面这个例子很好的演示了AttachCurrentThread/DetachCurrentThread的用法:

#include <jni.h>
#include <pthread.h>
#include <android/log.h>

extern JavaVM *g_vm;
JNIEnv* getEnv();

void* __start_routine(void*) {
    JNIEnv *env1, *env2, *env3, *env4, *env5;
    int ret;

    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_4;
    args.name = "pthread-test";//给线程起个名字吧,这样在调试或者崩溃的时候能显示出名字,而不是thead-1,thread-2这样的名字。
    args.group = NULL;//java.lang.ThreadGroup的全局引用,作用你懂的。

    //在调用AttachCurrentThread以前,是没有java环境的,所以GetEnv返回的JNIEnv是NULL
    g_vm->GetEnv((void**)&env1,JNI_VERSION_1_4);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "before AttachCurrentThread env is:%p", env1);

    //调用AttachCurrentThread,将当前线程附加到虚拟机,附加成功后,将会返回JNIEnv
    ret = g_vm->AttachCurrentThread(&env2, &args);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "do     AttachCurrentThread env is:%p, ret=%d", env2, ret);


    //在调用AttachCurrentThread以后,GetEnv返回了正确的JNIEnv
    g_vm->GetEnv((void**)&env3,JNI_VERSION_1_4);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "after  AttachCurrentThread env is:%p", env3);

    //再次调用AttachCurrentThread,直接返回JNIEnv,作用相当于GetEnv
    ret = g_vm->AttachCurrentThread(&env4, NULL);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "retry  AttachCurrentThread env is:%p, ret=%d", env4, ret);

    //从虚拟机分离线程
    g_vm->DetachCurrentThread();

    //在调用DetachCurrentThread以后,GetEnv返回NULL
    g_vm->GetEnv((void**)&env5,JNI_VERSION_1_4);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "after  DetachCurrentThread env is:%p", env5);
    return NULL;
}

extern "C" JNIEXPORT void Java_com_example_threadtest_PThread_start(JNIEnv *env, jclass clazz) {
    pthread_t thread;
    pthread_create(&thread, NULL, __start_routine, NULL);
}
posted @ 2022-10-17 10:29  咸鱼Jay  阅读(1243)  评论(0编辑  收藏  举报
页脚HTML代码