JNI与多个线程导致JNIEnv*发生异常
来源:互联网 发布:淘宝企业店铺转让过程 编辑:程序博客网 时间:2024/06/02 12:44
1.概述:
JNIEnv 是一个与线程相关的变量,即线程A有一个 JNIEnv变量, 线程B也有一个JNIEnv变量,由于线程相关,所以A线程不能使用B线程的 JNIEnv 结构体变量。
2.问题描述:
一个java对象通过JNI调用DLL中一个send()函数向服务器发送消息,不等服务器消息到来就立即返回,同时把JNI接口的指针JNIEnv *env(虚拟机环境指针),和jobject obj保存在DLL中的变量里.一段时间后,DLL中的消息接收线程接收到服务器发来的消息,并试图通过保存过的env和obj来调用先前的java对象的方法(相当于JAVA回调方法)来处理此消息此时程序会突然退出(崩溃).
即前台JAVA线程发送消息,后台线程处理消息,归属于两个不同的线程,不能使用相同的JNIEnv变量,这里可以利用一个机制: 利用全局的 JavaVM * 指针得到当前线程的 JNIEnv* 指针,与在C++中两个线程使用TLS进行局部存储类似的原理。
3.具体方法:
获取全局的JavaVM变量:
/* Our VM */JavaVM *g_vm;env->GetJavaVM(&g_vm); //来获取JavaVM指针.获取了这个指针后,将该JavaVM保存起来。线程 JNIEnv 指针,线程中获取 JNIEnv 方法:
JNIEnv *e;JavaVMAttachArgs thread_args;thread_args.name = "NFC Message Loop";thread_args.version = nat->env_version;thread_args.group = NULL;g_vm->AttachCurrentThread(&e, &thread_args); //后面的参数可以传空while(1){//...}g_vm->DetachCurrentThread(); //使用完成后
经过如此以后,JNIEnv 就可以由每个线程独自使用了。
而如果我们需要回调JAVA方法,jobject 也不能在多个线程中共享,如此可以在多个线程中使用了:
gs_object=env->NewGlobalRef(obj);//创建一个全局变量
将传入的obj(局部变量)保存到gs_object中,从而其他线程可以使用这个gs_object(全局变量)来操纵这个java对象了
完整示例代码如下:
java代码:Test.java:
[java]
import java.io.*; class Test implements Runnable { public int value = 0; static{ System.loadLibrary("Test");} public native void setEnev();//本地方法 public static void main(String args[]) throws Exception { Test t = new Test(); <span style="color:#FF0000;"> t.setEnev(); //调用本地方法 </span> while(true) { Thread.sleep(1000); System.out.println(t.value); } } } import java.io.*; class Test implements Runnable { public int value = 0; static{ System.loadLibrary("Test");} public native void setEnev();//本地方法 public static void main(String args[]) throws Exception { Test t = new Test(); <span style="color:#FF0000;"> t.setEnev(); //调用本地方法 </span> while(true) { Thread.sleep(1000); System.out.println(t.value); } } }
JNI代码 Test.cpp:
static JavaVM *gs_jvm=NULL; static jobject gs_object=NULL; static int gs_i=10; JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj) { env->GetJavaVM(&gs_jvm); //保存到全局变量中JVM //直接赋值obj到DLL中的全局变量是不行的,应该调用以下函数: gs_object=env->NewGlobalRef(obj); HANDLE ht=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFun,0,NULL,NULL); }void WINAPI ThreadFun(PVOID argv)//JNI中线程回调这个方法 { JNIEnv *env; gs_jvm->AttachCurrentThread((void **)&env, NULL); jclass cls = env->GetObjectClass(gs_object); //获取JAVA线程中的全局对象 jfieldID fieldPtr = env->GetFieldID(cls,"value","I"); // 获取JAVA对象 while(1) { Sleep(100); //这里改变JAVA对象的属性值(回调JAVA) env->SetIntField(gs_object,fieldPtr,(jint)gs_i++); } }4.总结:
对于如上的思路,只要你理解了TLS的用法就很容易理解以上内容了。
附加介绍 TLS (thread-local storage) 一下,网上摘抄的内容:
线程是执行的单元,同一个进程内的多个线程共享了进程的地址空间,线程一般有自己的栈,但是如果想要实现某个全局变量在不同的线程之间取不同的值,而且不受影响。一种办法是采用线程的同步机制,如对这个变量的读写之处加临界区或者互斥量,但是这是以牺牲效率为代价的,能不能不加锁呢?线程局部存储就是干这个的。
Windows中是根据线程局部存储索引来标识的(这个标识的分配和释放由TlsAlloc和TlsFree完成),有了个这个”标识“就可以在各个线程中调用TlsGetValue或者TlsSetValue读取或者设置各线程各自的值;
DWORD TlsAlloc(void); <pre name="code" class="cpp" style="color: rgb(51, 51, 51); font-size: 14px; line-height: 28px; text-indent: 28px; ">DWORD TlsAlloc(void);
<span style="font-family: 宋体; text-indent: 2em; ">BOOL TlsFree(DWORD dwTlsIndex);</span>LPVOID TlsGetValue(DWORD dwTlsIndex);BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue);
linux 平台对应的接口函数:
<div style="text-align: left;"><span style="font-family: 宋体; text-indent: 2em; ">int pthread_key_create(pthread_key_t * key, void (*)(void *));</span></div>int pthread_key_delete(pthread_key_t);void *pthread_getspecific(pthread_key_t);int pthread_setspecific(pthread_key_t, const void *);
- JNI与多个线程导致JNIEnv*发生异常
- Android JNI 跨线程使用JniEnv
- jni 新线程使用JNIEnv *env
- JNI|在子线程中获得JNIEnv|AttachCurrentThread
- JNI|在子线程中获得JNIEnv|AttachCurrentThread
- JNI全局对象,及原生线程JNIENV传递
- Android之jni解决JNIEnv跨线程问题
- JNI初体验,JNIEnv类型分析与介绍
- JNI完全指南(十)——JavaVM与JNIEnv
- jni之JNIEnv*
- jni 介绍 包含JNIEnv
- JNI总管:JNIEnv
- Android JNI之JNIEnv
- JNI总管:JNIEnv
- JNI多个线程中使用
- Chap16:JNI的c代码中,另外一个线程获取 JNIEnv
- jni开发strcat导致的异常
- 访问JNI函数:JNIEnv自变量
- 关于std::map中的find和[]的问题
- CentOS下挂载U盘
- js判断各个浏览器的方法
- 自定义涂鸦DEMO
- 在Linux终端下调用可执行文件时总要加上符号./的原因
- JNI与多个线程导致JNIEnv*发生异常
- 微定手机定位软件,IOS版新鲜出炉!
- java基本文件的读写
- SSH框架搭建过程---之Struts整合Spring框架(4)
- asp.net Cookie的存储
- sigsuspend()
- POJ-2481
- python 正则表达式判断邮箱格式是否正确
- 【IPV6基础知识】IPV6地址分配机制