JNI多個線程中使用

來源:互聯網
上載者:User

上一篇文章http://www.bkjia.com/kf/201202/119073.html說到 JNIEnv 是一個與線程相關的變數,即線程A有一個 JNIEnv變數, 線程B也有一個JNIEnv變數,由於線程相關,所以A線程不能使用B線程的 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儲存起來。

 


線程 JNIEnv 指標,線程中擷取 JNIEnv 方法:

   JNIEnv *e;
   JavaVMAttachArgs thread_args;


 thread_args.name = "NFC Message Loop";
   thread_args.version = nat->env_version;
   thread_args.group = NULL;

   g_vm->AttachCurrentThread(&e, &thread_args); //後面的參數可以傳空

while(1){

//...

g_vm->DetachCurrentThread(); //使用完成後

 


經過如此以後,JNIEnv 就可以由每個線程獨自使用了。

 


而如果我們需要回調JAVA方法,jobject 也不能在多個線程中共用,如此可以在多個線程中使用了:

gs_object=env->NewGlobalRef(obj);//建立一個全域變數


將傳入的obj(局部變數)儲存到gs_object中,從而其他線程可以使用這個gs_object(全域變數)來操縱這個java對象了

完整範例程式碼如下:

java代碼:Test.java:


[java]
import java.io.*;   
class Test implements Runnable   
{   
 public int value  = 0;   
 static{ System.loadLibrary("Test");}   
   
 public native void setEnev();//本地方法     
   
public static void main(String args[]) throws Exception   
 {   
   Test t = new Test();   
<span style="color:#FF0000;">   t.setEnev(); //調用本地方法   </span>  
   
    while(true)   
    {    
      Thread.sleep(1000);   
      System.out.println(t.value);   
    }   
  }   

import java.io.*; 
class Test implements Runnable 

 public int value  = 0; 
 static{ System.loadLibrary("Test");} 
 
 public native void setEnev();//本地方法  
 
public static void main(String args[]) throws Exception 
 { 
   Test t = new Test(); 
<span style="color:#FF0000;">   t.setEnev(); //調用本地方法   </span>
 
    while(true) 
    {  
      Thread.sleep(1000); 
      System.out.println(t.value); 
    } 
  } 
}

 


JNI代碼 Test.cpp:
static JavaVM *gs_jvm=NULL; 
static jobject gs_object=NULL; 
static int gs_i=10; 
 
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); 
}


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++); 
  } 
}

 

對於如上的思路,只要你理解了TLS的用法就很容易理解以上內容了。

附加介紹 TLS (thread-local storage) 一下,網上摘抄的內容:

線程是執行的單元,同一個進程內的多個線程共用了進程的地址空間,線程一般有自己的棧,但是如果想要實現某個全域變數在不同的線程之間取不同的值,而且不受影響。一種辦法是採用線程的同步機制,如對這個變數的讀寫之處加臨界區或者互斥量,但是這是以犧牲效率為代價的,能不能不加鎖呢?線程局部儲存就是幹這個的。

 

Windows中是根據線程局部儲存索引來標識的(這個標識的分配和釋放由TlsAlloc和TlsFree完成),有了個這個”標識“就可以在各個線程中調用TlsGetValue或者TlsSetValue讀取或者設定各線程各自的值;

DWORD TlsAlloc(void); 


BOOL TlsFree(DWORD dwTlsIndex);
LPVOID TlsGetValue(DWORD dwTlsIndex);
BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue);

 


linux 平台對應的介面函數:

int pthread_key_create(pthread_key_t * key, void (*)(void *));
int pthread_key_delete(pthread_key_t);
void *pthread_getspecific(pthread_key_t);
int pthread_setspecific(pthread_key_t, const void *);

摘自 andyhuabing的專欄

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.