<script type="text/javascript"> document.body.oncopy = function() { if (window.clipboardData) { setTimeout(function() { var text = clipboardData.getData("text"); if (text && text.length > 300) { text = text + "/r/n/n本文來自CSDN博客,轉載請標明出處:" + location.href; clipboardData.setData("text", text); } }, 100); } } </script>
java中要訪問C++代碼時, 使用JNI是唯一選擇. 然而,在多線程的情況下, 可能出現以下問題:
問題描述:
一個java對象通過JNI調用DLL中一個send()函數向服務器發送消息,不等服務器消息到來就立即返回.同時
把JNI接口的指針JNIEnv *env,和jobject obj保存在DLL中的變量裏.
一段時間後,DLL中的消息接收線程接收到服務器發來的消息,
並試圖通過保存過的env和obj來調用先前的java對象的方法來處理此消息.
然而,JNI文檔上說,JNI接口的指針JNIEnv*不能在c++的線程間共享,
在我的程序中,如果接收線程試圖調用java對象的方法,程序會突然退出.
不知道有沒有方法突破JNI接口的指針不能在多個c++線程中共享的限制?
解決辦法:
在 http://java.sun.com/docs/books/jni/html/pitfalls.html#29161
提到,
JNI接口指針不可爲多個線程共用,但是java虛擬機的JavaVM指針是整個jvm公用的. 於是,在DLL中可以調用:
static JavaVM* gs_jvm;
env->GetJavaVM(&gs_jvm); 來獲取JavaVM指針.獲取了這個指針後,在DLL中的另一個線程裏,可以調用:
JNIEnv *env;
gs_jvm->AttachCurrentThread((void **)&env, NULL);
來將DLL中的線程 "attached to the virtual machine"(不知如何翻譯...),同時獲得了這個線程在jvm中的 JNIEnv指針.
由於我需要做的是在DLL中的一個線程裏改變某個java對象的值,所以,還必須獲取那個java對象的jobject指針.同 JNIEnv
指針一樣,jobject指針也不能在多個線程中共享.
就是說,不能直接在保存一個線程中的jobject指針到全局變量中,然後在另外一個線程中使用它.幸運的是,可以用
gs_object=env->NewGlobalRef(obj);
來將傳入的obj保存到gs_object中,從而其他線程可以使用這個gs_object來操縱那個java對象了.
示例代碼如下:
(1)java代碼:Test.java:
import java.io.*;
class Test implements Runnable
{
public int value = 0;
private Thread tx=null;
public Test()
{
tx=new Thread(this,"tx");
}
static
{
System.loadLibrary("Test");
}
public native void setEnev();
public static void main(String args[])
{
Test t = new Test();
t.setEnev();
System.out.println("ok in java main");
t.tx.start();
try
{
Thread.sleep(10000000);
}catch(Exception e)
{
System.out.println("error in main");
}
}
public void run()
{
try
{
while(true)
{
Thread.sleep(1000);
System.out.println(value);
}
}catch(Exception e)
{
System.out.println("error in run");
}
}
}
(2) DLL代碼:Test.cpp:
#include "test.h"
#include<windows.h>
#include<stdio.h>
static JavaVM *gs_jvm=NULL;
static jobject gs_object=NULL;
static int gs_i=10;
void WINAPI ThreadFun(PVOID argv)
{
JNIEnv *env;
gs_jvm->AttachCurrentThread((void **)&env, NULL);
jclass cls = env->GetObjectClass(gs_object);
jfieldID fieldPtr = env->GetFieldID(cls,"value","I");
while(1)
{
Sleep(100);
//在DLL中改變外面的java對象的value變量的值.
env->SetIntField(gs_object,fieldPtr,(jint)gs_i++);
}
}
JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj)
{
printf("come into test.dll/n");
//Returns “0” on success; returns a negative value on failure.
int retGvm=env->GetJavaVM(&gs_jvm);
//直接保存obj到DLL中的全局變量是不行的,應該調用以下函數:
gs_object=env->NewGlobalRef(obj);
HANDLE ht=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFun,0,NULL,NULL);
printf("the Handle ht is:%d/n",ht);
}
引用自:http://blog.csdn.net/hust_liuX/archive/2006/12/25/1460486.aspx