一、Native 程式如何誕生子線程 在Java層的各進程(Process) 都有主線程(Main Thread),各線程皆可能誕生子線程。這些Java層的線程都有可能並行呼叫同一個Native函數,因而Native函數的線程安全考量是必要的。 Java線程一旦進入Native函數裡執行,在其執行過程中,也可能會誕生子線程,也可能多個線程並行執行同一個非Native的一般C/C++函數。該如何面對他們之間的線程安全問題呢?先談談如何誕生Native子線程呢? 請看範例: /* com_misoo_thread_JTX05.cpp */ #include "com_misoo_thread_JTX05.h" #include <utils/Log.h> #include <utils/IPCThreadState.h> #include <utils/ProcessState.h> #include "android_runtime/AndroidRuntime.h" using namespace android; JavaVM *gJavaVM; jmethodID mid; jclass mClass; // Reference to JTX05 class jobject mObject; // Weak ref to JTX05 Java object to call on char sTid[20]; unsigned int e1; int x; int sum; pthread_t thread; void* trRun( void* ); typedef multimap<string, JNIGlobalRef<jobject> *>MapOfObjects; JNIEXPORT jstring JNICALL Java_com_misoo_thread_JTX05_execute(JNIEnv *env, jobject thiz,jobject syncObj){ ……… int rr = pthread_create( &thread, NULL,trRun, NULL); ……… } void* trRun( void* ) { ……… } 使用函數pthread_create()函數來誕生Native層的子線程。 二、多個Native線程的安全問題 由於Native函數裡執行的線程也能誕生子線程,所以也應該注意其線程安全問題。例如: // JTX07.java package com.misoo.thread; import java.lang.ref.WeakReference; import com.misoo.pk01.ac01; import android.os.Handler; import android.os.Message; import android.os.Process; public class JTX07 { private long refer; private static Handler h; private static int count; static { System.loadLibrary("JTX07_jni"); } public JTX07(){ init(new WeakReference<JTX07>(this)); count = 0; h = new Handler(){ public void handleMessage(Message msg) { count++; if(count == 1) { ac01.ref.setTitle("env: " +String.valueOf(msg.arg1)); } else if(count == 2) ac01.tv.setText("env: " +String.valueOf(msg.arg1)); } }; } public long calculate(){ execute(this); //this.ssetTitle(ss); //ac01.ref.setTitle("ss:" + ss); return 0; } @SuppressWarnings("unused") private static voidcallback(int a, int b){ Message m = h.obtainMessage(1, a, 3,null); h.sendMessage(m); } private native voidinit(Object weak_this); private native Stringexecute(Object oSync); //private native Stringexecute(int x); } /* com_misoo_thread_JTX07.h */ /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_misoo_thread_JTX07 */ #ifndef _Included_com_misoo_thread_JTX07 #define _Included_com_misoo_thread_JTX07 #ifdef __cplusplus extern "C" { #endif /* * Class: com_misoo_thread_JTX07 * Method: nativeSetup * Signature:(Ljava/lang/Object;)V */ void JNICALL Java_com_misoo_thread_Init (JNIEnv *,jobject, jobject); /* * Class: com_misoo_thread_JTX07 * Method: execute * Signature:(J)J */ jstring JNICALL Java_com_misoo_thread_Exec (JNIEnv *,jobject, jobject); #ifdef __cplusplus } #endif #endif /* com_misoo_thread_JTX07.cpp */ #include "com_misoo_thread_JTX07.h" #include <utils/Log.h> #include <utils/IPCThreadState.h> #include <utils/ProcessState.h> #include "android_runtime/AndroidRuntime.h" using namespace android; JavaVM *gJavaVM; jmethodID mid; jclass mClass; // Reference to JTX07class jobject mObject; // Weak ref to JTX07 Javaobject to call on char sTid[20]; unsigned int e1; int x; int sum; pthread_t thread; void* trRun( void* ); void callBack(JNIEnv *); jobject mSyncObj; //typedef multimap<string,JNIGlobalRef<jobject> *> MapOfObjects; //-------------------------------------------------------------- void Thread_sleep(int t) { timespec ts; ts.tv_sec = t; ts.tv_nsec =0; nanosleep(&ts, NULL); return; } void JNICALL Java_com_misoo_thread_setUp(JNIEnv *env, jobject thiz,jobject weak_this) { jclass clazz =env->GetObjectClass(thiz); mClass =(jclass)env->NewGlobalRef(clazz); mObject = env->NewGlobalRef(weak_this); mid =env->GetStaticMethodID(mClass, "callback","(II)V"); return; } jstring JNICALL Java_com_misoo_thread_Exec(JNIEnv *env, jobject thiz,jobject syncObj){ mSyncObj =env->NewGlobalRef(syncObj); int rr =pthread_create( &thread, NULL, trRun, NULL); Thread_sleep(4); callBack(env); //----------------------------------------------------------- long pid =getpid(); sprintf(sTid,"%lu", pid); jstring ret =env->NewStringUTF(sTid); return ret; } //-------------------------------------------------------------------- void callBack(JNIEnv *env){ env->MonitorEnter(mSyncObj); sum = 0; for(int i = 0;i<=10; i++) { sum +=i; Thread_sleep(1); } //----------------------------------------------------------- env->CallStaticVoidMethod(mClass, mid, sum, 666); //----------------------------------------------------------- env->MonitorExit(mSyncObj); } //-------------------------------------------------------------------- static const char *classPathName ="com/misoo/thread/JTX07"; static JNINativeMethod methods[] = { {"init", "(Ljava/lang/Object;)V", (void*)Java_com_misoo_thread_setUp}, {"execute", "(Ljava/lang/Object;)Ljava/lang/String;", (void *)Java_com_misoo_thread_Exec} }; /* * Registerseveral native methods for one class. */ static int registerNativeMethods(JNIEnv* env, constchar* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz =env->FindClass(className); if (clazz ==NULL) { LOGE("Native registration unable to find class '%s'",className); return JNI_FALSE; } if(env->RegisterNatives(clazz, gMethods, numMethods) < 0) { LOGE("RegisterNatives failed for '%s'", className); returnJNI_FALSE; } returnJNI_TRUE; } static int registerNatives(JNIEnv* env) { if(!registerNativeMethods(env, classPathName, methods, sizeof(methods) / sizeof(methods[0]))) { returnJNI_FALSE; } returnJNI_TRUE; } //---------------------------------------------------------------------------- jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv *env; gJavaVM = vm; int result; LOGI("JNI_OnLoad called"); if (vm->GetEnv((void**) &env, JNI_VERSION_1_4)!= JNI_OK) { LOGE("Failed to get the environment using GetEnv()"); return -1; } if (registerNatives(env) != JNI_TRUE) { LOGE("ERROR: registerNatives failed"); gotobail; } result =JNI_VERSION_1_4; bail: returnresult; } //-------------------------------------------------------------------------------- void* trRun( void* ) { int status; JNIEnv *env; boolisAttached = false; Thread_sleep(1); status =gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4); if(status< 0) { LOGE("callback_handler: failed to get JNI environment, " "assuming native thread"); status =gJavaVM->AttachCurrentThread(&env, NULL); if(status< 0) { LOGE("callback_handler: failed to attach " "current thread"); return NULL; } isAttached = true; } //----------------------------------------------------------- callBack(env); //----------------------------------------------------------- if(isAttached) gJavaVM->DetachCurrentThread(); return NULL; } 主線程誕生了子線程去執行trRun()函數。必須先調用: gJavaVM->AttachCurrentThread(&env, NULL); 才能取得子線程自己所屬的JNIEnv物件之參考了,並且呼叫Callback()函數。之後,主線程也呼叫同一Callback函數。於是,在Callback()函數裡,使用env->MonitorEnter()和env->MonitorExit(mSyncObj);指令來讓各線程能達到同步。