java中要訪(fǎng)問(wèn)C++代碼時(shí), 使用JNI是唯一選擇. 然而,在多線(xiàn)程的情況下, 可能出現以下問(wèn)題:
問(wèn)題描述:
一個(gè)java對象通過(guò)JNI調用DLL中一個(gè)send()函數向服務(wù)器發(fā)送消息,不等服務(wù)器消息到來(lái)就立即返回.同時(shí)
把JNI接口的指針JNIEnv *env,和jobject obj保存在DLL中的變量里.
一段時(shí)間后,DLL中的消息接收線(xiàn)程接收到服務(wù)器發(fā)來(lái)的消息,
并試圖通過(guò)保存過(guò)的env和obj來(lái)調用先前的java對象的方法來(lái)處理此消息.
然而,JNI文檔上說(shuō),JNI接口的指針JNIEnv*不能在c++的線(xiàn)程間共享,
在我的程序中,如果接收線(xiàn)程試圖調用java對象的方法,程序會(huì )突然退出.
不知道有沒(méi)有方法突破JNI接口的指針不能在多個(gè)c++線(xiàn)程中共享的限制?
解決辦法:
在 http://java.sun.com/docs/books/jni/html/pitfalls.html#29161 提到,
JNI接口指針不可為多個(gè)線(xiàn)程共用,但是java虛擬機的JavaVM指針是整個(gè)jvm公用的. 于是,在DLL中可以調用:
static JavaVM* gs_jvm;
env->GetJavaVM(&gs_jvm); 來(lái)獲取JavaVM指針.獲取了這個(gè)指針后,在DLL中的另一個(gè)線(xiàn)程里,可以調用:
JNIEnv *env;
gs_jvm->AttachCurrentThread((void **)&env, NULL);
來(lái)將DLL中的線(xiàn)程 "attached to the virtual machine"(不知如何翻譯...),同時(shí)獲得了這個(gè)線(xiàn)程在jvm中的 JNIEnv指針.
由于我需要做的是在DLL中的一個(gè)線(xiàn)程里改變某個(gè)java對象的值,所以,還必須獲取那個(gè)java對象的jobject指針.同 JNIEnv 指針一樣,jobject指針也不能在多個(gè)線(xiàn)程中共享. 就是說(shuō),不能直接在保存一個(gè)線(xiàn)程中的jobject指針到全局變量中,然后在另外一個(gè)線(xiàn)程中使用它.幸運的是,可以用
gs_object=env->NewGlobalRef(obj);
來(lái)將傳入的obj保存到gs_object中,從而其他線(xiàn)程可以使用這個(gè)gs_object來(lái)操縱那個(gè)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);
}
(###)
聯(lián)系客服