JNI 多線程的處理

<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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章