chap 17:当JNI遇到多线程--java对象如何被C++中的多个线程访问?

来源:互联网 发布:中原工学院网络教学 编辑:程序博客网 时间:2024/05/05 19:32

java中要访问C++代码时,使用JNI是唯一选择.然而,在多线程的情况下,可能出现以下问题:

问题描述:

一个java对象通过JNI调用DLL中一个send()函数向服务器发送消息,不等服务器消息到来就立即返回.同时把JNI接口的指针JNIEnv *env,jobject obj保存在DLL中的变量里.

一段时间后,DLL中的消息接收线程接收到服务器发来的消息,并试图通过保存过的envobj来调用先前的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 tothe virtual machine"(不知如何翻译...),同时获得了这个线程在jvm中的  JNIEnv指针.

由于我需要做的是在DLL中的一个线程里改变某个java对象的值,所以,还必须获取那个java对象的jobject指针. JNIEnv指针一样,jobject指针也不能在多个线程中共享.就是说,不能直接在保存一个线程中的jobject指针到全局变量中,然后在另外一个线程中使用它.幸运的是,可以用

gs_object=env->NewGlobalRef(obj);

来将传入的obj保存到gs_object,从而其他线程可以使用这个gs_object来操纵那个java对象了.

示例代码如下:

(1)java代码:

//file name: 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 voidsetEnev();

 public static void main(Stringargs[])

 {

  Test t = new Test();

  t.setEnev();

  System.out.println("ok in javamain");

  t.tx.start ();

  try

  {

   Thread.sleep(10000000);

  }catch(Exception e)

  {

   System.out.println("errorin main");

  }

 }

 public void run()

 {

  try

  {

    while(true)

    {

     Thread.sleep(1000);

     System.out.println(value);

    }

  }catch(Exception e)

  {

    System.out.println("errorin run");

  } 

 }

}

(2) DLL代码:

 

//cpp file name:  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 JNICALLJava_Test_setEnev(JNIEnv *env, jobject obj)

{

 printf("come intotest.dll\n");

 //Returns “0” on success; returns anegative value on failure.

 intretGvm=env->GetJavaVM(&gs_jvm);

 //直接保存objDLL中的全局变量是不行的,应该调用以下函数:

 gs_object=env->NewGlobalRef(obj);

 HANDLE ht=CreateThread( NULL,0,

(LPTHREAD_START_ROUTINE)ThreadFun,0,

NULL,NULL);

 printf("the Handle htis:%d\n",ht);

}


原创粉丝点击