在Android中,因爲性能等多方面因素,多線程使用的場景較多,基於多線程的消息機制Handler、異步處理AsyncTask、回調方法等也常常會遇到,這裏簡要分析下Java多線程的使用和原理(針對Thread和Runnable,Callable等不在討論範圍內)html
java端多線程的使用比較簡單,JDK提供了接口Runnable和類Thread以供多線程使用,實現run方法,執行start函數啓動便可,網上例子不少,這邊給出最簡單的使用:java
//1.繼承Thread類 public class MyThread extends Thread{ @Override public void run(){ ... } } MyThread mThread = new MyThread(); mThread.start(); //2.實現Runnable接口 public class MyRunnable implements Runnable { @Override public void run(){ ... } } Thread thread = new Thread(new MyRunnable()); thread.start();
衆所周知,重寫run方法,就是肯定線程的執行方法;而調用start函數,就是啓動多線程執行。若是隻調用run方法,和調用正常函數並沒有區別,仍是在當前線程執行。c++
之因此在實現Runnable接口以後還須要定義Thread對象來調用start函數,就是由於Runnable接口沒有start函數 (接口的方法是抽象方法,不能有具體實現,必須在子類覆蓋實現,而本身實現start函數又不怎麼現實),因此只能調用其子類Thread提供的start完成多線程的建立執行。數組
這裏給出Thread和Runnable的部分源碼:安全
//Runnable接口很是簡單,只有一個虛方法run public interface Runnable { public abstract void run(); } //Thread類實現了Runnable,並提供了start方法(下面具體分析) public class Thread implements Runnable { public synchronized void start() { ... } private Runnable target; @Override public void run() { //若是Thread的run未被重寫,且Runnable對象不爲空,則調用Runnable的run //然而Runnable是接口,不能實例化,run方法也不能實現 //這種狀況其實就是定義了類A實現了Runnable接口,並用類A的對象做爲參數建立了Thread對象 if (target != null) { target.run(); } } }
除了經過定義子類的方式實現多線程,固然也能夠經過使用匿名內部類,一樣是依據類Thread或接口Runnable多線程
public static void main(String[] args) { //3.匿名內部類繼承thread new Thread() { public void run() { ... } }.start(); //4.匿名內部類實現runnable,一樣依賴於Thread的start建立執行線程 new Thread(new Runnable() { @Override public void run() { ... } }).start(); }
從上面的使用實例能夠看出,多線程的啓動執行工做就在這個start函數中,下面來具體看看app
首先看下start函數在Thread.java中的定義異步
public class Thread implements Runnable { public synchronized void start() { //防止一些由VM啓動的線程被人爲調用啓動(主線程或系統組線程) if (threadStatus != 0) throw new IllegalThreadStateException(); //group主要是對線程隊列的操做(記錄線程狀態,啓動、阻塞等的計數等),對線程自身的運行無關 group.add(this); boolean started = false; try { //線程啓動函數,native函數,具體實如今C++端 start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0(); }
通過一些異常處理和狀態記錄後,啓動操做交給底層的C++去實現,啓動函數就是這個start0。ide
按照openjdk的設計,java與c++相對應的類通常選擇相同名稱。查找後發如今/jdk/src/java.base/share/native/libjava文件夾下有一個同名文件Thread.c。函數
掃視一眼,Thread.c中確實有start0,但卻沒有遵循傳統的JNI調用函數命名規則(Java_包層級目錄_函數名),而是使用了對函數進行註冊的機制——RegisterNatives
RegisterNatives的做用就是向VM註冊 java方法<—>C++函數 的映射關係,以便在java端調用native函數時能夠快速地定位其對應的C++方法,並且便於修改:改變映射關係數組,再次調用RegisterNatives可覆蓋以前的映射關係,而傳統的JNI命名規則則須要修改C++方法
下面來看下Thread類是如何使用這個註冊機制的:
在java端調用native函數以前,須要主動調用RegisterNatives進行native函數的註冊,在Thread.java中,申明並調用了以下native函數
public class Thread implements Runnable { //系統註釋:確保這個函數是該接口初始化時第一個調用的 //這個函數是用於JNI關聯C++方法和native函數的,Thread中的線程操做函數並無使用JNI中傳統的名稱對應規則,因此須要用這個函數保證native函數有定義可用 private static native void registerNatives(); //放在static塊中,在初始化Thread類時執行一次註冊便可,與對象無關 static { registerNatives(); } } //對應的C++方法,使用了傳統的JNI調用函數命名規則,就在以前提到的Thread.c文件中 JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) { //向VM註冊native函數的對應關係,此函數具體源碼沒有找到,這個methods就是native函數的映射表,下面會講到,cls爲類型,對應Java中的Thread,這樣就能精肯定位Java中的函數 (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); }
native函數與C++方法的對應關係結構體及Thread所用到的函數映射表
//描述一個native函數對象和其對應的C++方法 typedef struct { char *name; //native函數名 char *signature; //native函數簽名(參數與返回值類型) void *fnPtr; //對應的函數指針引用(C++中的具體實現函數) } JNINativeMethod; //函數映射表methods static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"yield", "()V", (void *)&JVM_Yield}, {"sleep", "(J)V", (void *)&JVM_Sleep}, {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName}, };
通過RegisterNatives的註冊,Java端的native函數start0與C++端的JVM_StartThread造成對應關係,線程啓動工做也就落在了JVM_StartThread函數中
//宏JVM_ENTRY--JVM_END,用來對函數進行定義,這裏就是定義函數JVM_StartThread JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_StartThread"); JavaThread *native_thread = NULL; bool throw_illegal_thread_state = false; { //在java線程建立成功(加入到線程隊列)以前防止C++本地線程和相關平臺數據被釋放(平臺數據用於建立線程時選擇對應平臺方法,c++本地線程就是以後的操做線程) MutexLocker mu(Threads_lock); //防止對一個已存在的線程進行再次建立 if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) { throw_illegal_thread_state = true; } else { jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread)); size_t sz = size > 0 ? (size_t) size : 0; //建立本地線程(C++線程,根據不一樣平臺選擇不一樣的處理),設置線程處理函數爲thread_entry(下面會講到),這裏的JavaThread就是主要的線程處理類 //其屬性包括java層線程對象、jni指針、java層引用計數等以及一系列編譯優化內存控制項,控制本地線程的生命週期內活動、與Java端關聯等一系列操做(屬性、功能不少) native_thread = new JavaThread(&thread_entry, sz); //通過上面的建立JavaThread對象以後,對象native_thread中應該包含有平臺相關信息 if (native_thread->osthread() != NULL) { //將本地線程加入線程鏈表、設置優先級,並與java線程對象相關聯 //做爲參數的jthread其實就是java層調用start0的Thread類對象 //這裏經過句柄將jthread和C++線程關聯,經過JNI可直接操做C++線程 native_thread->prepare(jthread); } } } if (throw_illegal_thread_state) { THROW(vmSymbols::java_lang_IllegalThreadStateException()); } assert(native_thread != NULL, "Starting null thread?"); if (native_thread->osthread() == NULL) { delete native_thread; if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, os::native_thread_creation_failed_msg()); } THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), os::native_thread_creation_failed_msg()); } //本地線程在建立JavaThread對象時已經建立並初始化,在prepare時已與Java對象關聯 //線程在初始化狀態默認被阻塞,在這裏主要功能就是喚醒本地線程使其開始運行 Thread::start(native_thread); JVM_END
從上面的代碼中能夠看到,除了一些異常處理外,使用到的重要函數有三個,分別是JavaThread、prepare和start。他們的基本功能已經做了相關注釋,下面來具體看下
JavaThread這個類至關的大,功能至關的多,呵呵!在這裏只簡述下主要流程
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : Thread() { //初始化 initialize(); //JNI附加狀態,剛開始爲未附加(此時線程還沒有建立,固然未關聯) _jni_attach_state = _not_attaching_via_jni; //設置屬性(線程函數)_entry_point爲&thread_entry (參數entry_point對應的值爲&thread_entry),後面會用到 set_entry_point(entry_point); //設置線程類型(便宜線程或處理線程) os::ThreadType thr_type = os::java_thread; thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : os::java_thread; //屬性值設置完了,那毫無疑問最後這個函數就是最重要的線程建立函數了 //注意到函數前的做用域os沒,他就是操做系統接口類,提供基於平臺的代碼,也就是說,create_thread會根據平臺不一樣而不一樣,這裏主要介紹下Linux平臺下的相關代碼 os::create_thread(this, thr_type, stack_sz); }
//Linux平臺下對應的create_thread實現 bool os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size) { assert(thread->osthread() == NULL, "caller responsible"); OSThread* osthread = new OSThread(NULL, NULL); if (osthread == NULL) { return false; } //設置線程類型 osthread->set_thread_type(thr_type); //設置狀態(內存分配、初始化等狀態) osthread->set_state(ALLOCATED); //設置平臺相關數據 thread->set_osthread(osthread); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //堆棧大小設置什麼的就略過了 size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size); stack_size = align_size_up(stack_size + os::Linux::default_guard_size(thr_type), vm_page_size()); pthread_attr_setstacksize(&attr, stack_size); pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type)); ThreadState state; { pthread_t tid; //看到這句是否是很熟悉了,建立線程,處理函數爲thread_native_entry //相應的Windows下爲_beginthreadex;Solaris下爲thr_create int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread); char buf[64]; if (ret == 0) { log_info(os, thread)("Thread started (pthread id: " UINTX_FORMAT ", attributes: %s). ", (uintx) tid, os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr)); } else { log_warning(os, thread)("Failed to start thread - pthread_create failed (%s) for attributes: %s.", os::errno_name(ret), os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr)); } //刪除臨時數據、報錯退出 pthread_attr_destroy(&attr); if (ret != 0) { // Need to clean up stuff we've allocated so far thread->set_osthread(NULL); delete osthread; return false; } //向平臺數據記錄線程號 osthread->set_pthread_id(tid); //初始化成功或者停止 { Monitor* sync_with_child = osthread->startThread_lock(); MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag); while ((state = osthread->get_state()) == ALLOCATED) { sync_with_child->wait(Mutex::_no_safepoint_check_flag); } } } //達到上限,退出 if (state == ZOMBIE) { thread->set_osthread(NULL); delete osthread; return false; } //線程初始化成功,返回
//這裏有句系統註釋: The thread is returned suspended (in state INITIALIZED),也就是說,建立並初始化成功後,線程默認被阻塞,須要喚醒才能運行
assert(state == INITIALIZED, "race condition"); return true; }
通過函數create_thread處理以後,本地線程就已經建立成功並初始化,處理函數爲thread_native_entry。若是正確返回,那此時線程就處於已初始化狀態(此時線程還沒有加入到進程的線程鏈表中,且被阻塞),若是返回錯誤,那說明建立失敗。關於線程的狀態,能夠參考這篇文章。接下來的就是要看看thread_native_entry在哪定義,是否和咱們想的同樣,最終執行的是Java端所定義的函數run?
源碼中安全檢測類代碼太多,這邊就只列出主要函數
static unsigned __stdcall thread_native_entry(Thread* thread) { __try { thread->run(); } __except(topLevelExceptionFilter((_EXCEPTION_POINTERS*)_exception_info())) { } return (unsigned)os::win32::exit_process_or_thread(os::win32::EPT_THREAD, res); } void JavaThread::run() { thread_main_inner(); } void JavaThread::thread_main_inner() { if (!this->has_pending_exception() && !java_lang_Thread::is_stillborn(this->threadObj())) { { ResourceMark rm(this); this->set_native_thread_name(this->get_thread_name()); } HandleMark hm(this); this->entry_point()(this, this); } } ThreadFunction entry_point() const { return _entry_point; }
進過上面的流程過濾,最後定位到了函數 _entry_point,那這個是什麼呢?往回看一點點,對,就是在建立JavaThread的函數中,有一個set_entry_point(entry_point),功能是將_entry_point的值設置爲&thread_entry,也就是這邊所用的這個_entry_point,而其值就是&thread_entry,這個又是什麼呢?來看看定義
static void thread_entry(JavaThread* thread, TRAPS) { HandleMark hm(THREAD); Handle obj(THREAD, thread->threadObj()); JavaValue result(T_VOID); //JavaCalls: openjdk中用於C++端調用Java端方法的功能類,在這裏再也不深刻 //顯然這邊就是調用最終的具體執行函數了,來看看是否是run方法 //類vmSymbols是用於VM對所用的標識進行快速定位的,在他的命名空間中,定義有一系列(函數名,符號名)的對應關係 //類SystemDictionary用做類加載器的輔助類,記錄本地函數與Java類名的對應關係 JavaCalls::call_virtual(&result, obj, KlassHandle(THREAD, SystemDictionary::Thread_klass()), //類型java_lang_Thread的一個句柄 vmSymbols::run_method_name(), //函數名 vmSymbols::void_method_signature(), //函數簽名 THREAD); } //由此能夠看出,上面的call_virtual調用的Java端函數爲 void run();而函數的定位用到obj和KlassHandle //這個obj就是傳入的Java層線程對象,KlassHandle對應於類型java_lang_Thread的一個句柄 //call_virtual從名字就能夠看出是虛函數調用約定,後面會涉及連接時函數定位、運行時函數定位等等,有興趣的能夠查看源碼 template(run_method_name, "run") template(void_method_signature,"()V") //Pre表示該類爲預加載類,本地函數Thread_klass對應Java中的類java_lang_Thread do_klass(Thread_klass,java_lang_Thread,Pre)
到這裏,以前的new JavaThread(&thread_entry, sz)函數的功能大體已經清楚了,簡單的歸納就是:建立了一個本地線程,並使其處於已初始化狀態,線程處理函數爲Java層線程對象的run方法
prepare與start的篇幅比較少,放在一塊兒簡述下
void JavaThread::prepare(jobject jni_thread, ThreadPriority prio) { //保證當前線程佔有鎖定資源 assert(Threads_lock->owner() == Thread::current(), "must have threads lock"); //下面幾個操做將Java層線程對象和C++層本地線程相互關聯起來 //系統註釋中將jni_thread說成是C++線程對象,不甚理解,若是有大大懂得能夠說下~~下面按本身的理解來敘述 //將jni_thread(Java層線程對象)做爲本地線程的一個句柄,thread_oop則指向jni_thread,以後經過thread_oop就可調用Java層Thread類對象 Handle thread_oop(Thread::current(), JNIHandles::resolve_non_null(jni_thread)); assert(InstanceKlass::cast(thread_oop->klass())->is_linked(), "must be initialized"); //設置_threadObj(JavaThread中用於表示Java層線程對象的屬性),值爲上面定義的句柄,這樣就實現了C++調用Java的條件 set_threadObj(thread_oop()); //向java.lang.Thread對象的接口類註冊本地線程,這樣就實現了Java調用C++的條件 java_lang_Thread::set_thread(thread_oop(), this); if (prio == NoPriority) { prio = java_lang_Thread::priority(thread_oop()); assert(prio != NoPriority, "A valid priority should be present"); } //設置優先級 Thread::set_priority(this, prio); prepare_ext(); //將本地線程加入到線程隊列 Threads::add(this); }
用一句話來歸納prepare的工做:關聯Java層線程對象與C++本地線程,並將本地線程加入線程隊列
到如今爲止,就差一步線程就能開始工做了(還記得上面說的線程建立後默認被阻塞麼),最後的工做就是將它喚醒,固然就是start的功能了
void Thread::start(Thread* thread) { //啓動線程與恢復線程狀況不一樣,這邊要排除這種狀況 if (!DisableStartThread) { if (thread->is_Java_thread()) { //設置線程狀態爲運行 java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(), java_lang_Thread::RUNNABLE); } os::start_thread(thread); } } void os::start_thread(Thread* thread) { MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag); OSThread* osthread = thread->osthread(); //設置平臺相關數據的狀態爲運行 osthread->set_state(RUNNABLE); //調用各平臺的喚醒函數啓動線程 pd_start_thread(thread); } //Windows的比較直觀,分析Windows的 void os::pd_start_thread(Thread* thread) { //喚醒本地線程使之運行,對應Linxu的notify,Solaris的thr_continue DWORD ret = ResumeThread(thread->osthread()->thread_handle()); assert(ret != SYS_THREAD_ERROR, "StartThread failed"); }