二,解决方法
第一我们应该理解:
①(独立性) JNIEnv 是一个与线程相关的变量,即线程A有一个 JNIEnv变量, 线程B也有一个JNIEnv变量,由于线程相关,所以A线程不能使用B线程的 JNIEnv 结构体变量。 那么如何保证了每个线程JNIEnv的独立性呢?

一个java对象通过JNI调用DLL中一个send()函数向服务器发送消息,不等服务器消息到来就立即返回,同时把JNI接口的指针JNIEnv *env(虚拟机环境指针),和jobject obj保存在DLL中的变量里.一段时间后,DLL中的消息接收线程接收到服务器发来的消息,并试图通过保存过的env和obj来调用先前的java对象的方法(相当于JAVA回调方法)来处理此消息此时程序会 突然退出(崩溃).

即前台JAVA线程发送消息,后台线程处理消息,归属于两个不同的线程, 不能使用相同的JNIEnv变量,这里可以利用一个机制: 利用全局的 JavaVM * 指针得到当前线程的 JNIEnv* 指针 ,与在C++中两个线程使用TLS进行局部存储类似的原理。

具体方法:

获取全局的JavaVM变量:

/* Our VM */
JavaVM *g_vm;

env->GetJavaVM(&g_vm); //来获取JavaVM指针.获取了这个指针后,将该JavaVM保存起来。(转录)

static JavaVM* gJavaVM = NULL; // 定义一个全局Java VM引用对象 static jobject gJavaObj = NULL; // 定义一个全局Java object对象,对于java层的类对象
......

2其次在调用某个线程的函数中定义:(保证线程在进程中资源的公共性,这两句是把参数传给所开线程)

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);  
3在线程函数中 引用:  (保证线程函数 对应  env  和class)
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++);  

 三,我们在网上看到有些关于jni的程序资料用了(*env)->;

我们需要谨记:在linux下如果.c文件中用 “env->” 编译会找不到此结构,必须用“(*env)->”,或者改成.cpp文件,以 c++的方式来编译。

------------------------------------------------------------------------