Java并发编程—Thread类的start()方法是如何启动一个线程的?
目錄
一:Java線(xiàn)程介紹
二:Java線(xiàn)程入口分析
三:Java線(xiàn)程的創(chuàng)建
四:總結(jié)
周末抽了點(diǎn)時(shí)間,研究了下HotSpot是如何創(chuàng)建Java線(xiàn)程的,順便總結(jié)一下。文中引用的源碼里刪除很多細(xì)節(jié),只保留了主要過(guò)程,感興趣的同學(xué)可以自己去深入研究。能力有限,錯(cuò)誤地方懇請(qǐng)指正。
一:Java線(xiàn)程介紹
做Java開(kāi)發(fā)或者Android的同學(xué),肯定對(duì)Java線(xiàn)程很熟悉。之前在閱讀一些JVM相關(guān)的書(shū)時(shí),了解到Java線(xiàn)程其實(shí)是映射到操作系統(tǒng)的內(nèi)核線(xiàn)程上的,所以Java線(xiàn)程基本上也就是操作系統(tǒng)在進(jìn)行管理。在Linux中,線(xiàn)程和進(jìn)程用的是同一個(gè)結(jié)構(gòu)體進(jìn)行描述的,只不過(guò)進(jìn)程擁有自己獨(dú)立的地址空間,而同一個(gè)進(jìn)程的線(xiàn)程之間是共享資源的。
二:Java線(xiàn)程入口分析
想要啟動(dòng)一個(gè)Java線(xiàn)程,主要有兩種方法,第一種可以實(shí)現(xiàn)一個(gè)繼承自Thread的子類(lèi),重寫(xiě)run();第二種可以實(shí)現(xiàn)一個(gè)Runnable,交給Thread執(zhí)行。這兩種方法都很簡(jiǎn)便,我們可以根據(jù)自己的業(yè)務(wù)需求選擇,但是無(wú)論選擇哪種方法,都需要調(diào)用Thread.start(),才能真正的啟動(dòng)一個(gè)異步線(xiàn)程進(jìn)行工作,筆者剛上大學(xué)接觸Java時(shí),曾經(jīng)天真的以為調(diào)用Thread.run()就行了,其實(shí)直接調(diào)用run(),JVM并不會(huì)去創(chuàng)建一個(gè)線(xiàn)程,run()只會(huì)工作在原有線(xiàn)程,和調(diào)用普通對(duì)象的方法沒(méi)有任何區(qū)別。
上面提到了要想啟動(dòng)線(xiàn)程,必須要調(diào)用Thread.start(),那么這個(gè)方法便是我們研究的入口了。
public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */}}}private native void start0();通過(guò)上面的代碼可以得知,start()方法中做了一些線(xiàn)程狀態(tài)判斷等工作,但是真正啟動(dòng)Java線(xiàn)程的地方是調(diào)用了start0(),start0()是一個(gè)Native方法。start0()是何處實(shí)現(xiàn)的呢?我們先來(lái)看看Thread.c 中的一段定義:
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}, };上面定義了一個(gè)數(shù)組,數(shù)組中存放的為JNINativeMethod類(lèi)型的結(jié)構(gòu)體變量,JNINativeMethod定義在jni.h中:
typedef struct {char *name;char *signature;void *fnPtr; } JNINativeMethod;JNINativeMethod主要是進(jìn)行一個(gè)jni方法的映射關(guān)系,將native方法和真正的實(shí)現(xiàn)方法進(jìn)行綁定。那么具體是何時(shí)進(jìn)行綁定的呢?java層的Thread在類(lèi)的靜態(tài)初始化塊中,調(diào)用了registerNatives()方法:
private static native void registerNatives();static {registerNatives();}我們來(lái)看看registerNatives()對(duì)應(yīng)的Jni方法:
JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) {(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); }可見(jiàn)此時(shí)會(huì)將上述數(shù)組methods中的方法映射關(guān)系進(jìn)行注冊(cè)。注冊(cè)好了后,我們?cè)賮?lái)看看上面定義的數(shù)組,這里面將Java線(xiàn)程中的很多native方法與實(shí)現(xiàn)其功能的方法指針進(jìn)行了綁定,比如start0()就和JVM_StartThread進(jìn)行了綁定。所以我們想研究start0(),就直接看看JVM_StartThread指針?biāo)赶虻姆椒纯伞?/p>
三:Java線(xiàn)程的創(chuàng)建
JVM_StartThread定義在jvm.cpp中:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))JVMWrapper("JVM_StartThread");JavaThread *native_thread = NULL;bool throw_illegal_thread_state = false;{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;native_thread = new JavaThread(&thread_entry, sz);if (native_thread->osthread() != NULL) {native_thread->prepare(jthread);}}}......Thread::start(native_thread);JVM_END源碼中本來(lái)有很多英文注釋,我這里先給刪了,下面來(lái)一步步分析下上面的代碼吧。
1:判斷Java線(xiàn)程是否已經(jīng)啟動(dòng),如果已經(jīng)啟動(dòng)過(guò),則會(huì)拋異常。
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {throw_illegal_thread_state = true;}2: 如果第一步判斷中,Java線(xiàn)程沒(méi)有啟動(dòng)過(guò),則會(huì)開(kāi)始創(chuàng)建Java線(xiàn)程。
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread)); size_t sz = size > 0 ? (size_t) size : 0; native_thread = new JavaThread(&thread_entry, sz);Java線(xiàn)程的創(chuàng)建過(guò)程主要就在JavaThread的構(gòu)造函數(shù)中:
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :Thread() {initialize();_jni_attach_state = _not_attaching_via_jni;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(this, thr_type, stack_sz); }最后一句os::create_thread(this, thr_type, stack_sz) 便開(kāi)始真正的創(chuàng)建Java線(xiàn)程對(duì)應(yīng)的內(nèi)核線(xiàn)程。
bool os::create_thread(Thread* thread, ThreadType thr_type,size_t req_stack_size) {......pthread_t tid;int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);......return true; }上面這個(gè)方法主要就是利用pthread_create()來(lái)創(chuàng)建線(xiàn)程。其中第三個(gè)參數(shù)thread_native_entry便是新起的線(xiàn)程運(yùn)行的初始地址,其為定義在os_bsd.cpp中的一個(gè)方法指針,而第四個(gè)參數(shù)thread即thread_native_entry的參數(shù):
static void *thread_native_entry(Thread *thread) {......thread->run();......return 0; }新線(xiàn)程創(chuàng)建后就會(huì)從thread_native_entry()開(kāi)始運(yùn)行,thread_native_entry()中調(diào)用了thread->run():
// thread.cpp void JavaThread::run() {......thread_main_inner(); }此方法最后調(diào)用了thread_main_inner():
// thread.cpp 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);}DTRACE_THREAD_PROBE(stop, this);this->exit(false);delete this; }我們重點(diǎn)關(guān)注下this->entry_point()(this, this),entry_point()返回的其實(shí)就是在 new JavaThread(&thread_entry, sz) 時(shí)傳入的thread_entry。這里就相當(dāng)于調(diào)用了thread_entry(this,this)。thread_entry定義在jvm.cpp中:
// jvm.cpp static void thread_entry(JavaThread* thread, TRAPS) {HandleMark hm(THREAD);Handle obj(THREAD, thread->threadObj());JavaValue result(T_VOID);JavaCalls::call_virtual(&result,obj,KlassHandle(THREAD, SystemDictionary::Thread_klass()),vmSymbols::run_method_name(),vmSymbols::void_method_signature(),THREAD); }哈哈,這里見(jiàn)到了一個(gè)老朋友:JavaCalls。JavaCalls模塊是用來(lái)調(diào)用Java方法的。不了解的朋友可以直接看之前寫(xiě)的文章《JVM方法執(zhí)行的來(lái)龍去脈》。我們來(lái)看看這里調(diào)用JavaCalls::call_virtual()傳入的一些參數(shù):
- obj:Java線(xiàn)程對(duì)象;
- KlassHandle(THREAD, SystemDictionary::Thread_klass()):Java線(xiàn)程類(lèi),記錄在SystemDictionary中,即java_lang_Thread;
- vmSymbols::run_method_name():即"run";
- vmSymbols::void_method_signature():即"()V";
經(jīng)過(guò)上面的分析,這里其實(shí)就是開(kāi)始調(diào)用Java線(xiàn)程對(duì)象的run()方法。
3:開(kāi)始執(zhí)行所創(chuàng)建的內(nèi)核線(xiàn)程,即從第二步所說(shuō)的thread_entry處開(kāi)始執(zhí)行。
Thread::start(native_thread);四:總結(jié)
到這里Java線(xiàn)程就已經(jīng)真正的運(yùn)行起來(lái)了,總結(jié)下上面的過(guò)程:
- 1:調(diào)用Java線(xiàn)程start()方法,通過(guò)jni方式,調(diào)用到JVM層。
- 2:JVM通過(guò)pthread_create()創(chuàng)建一個(gè)系統(tǒng)內(nèi)核線(xiàn)程,并指定內(nèi)核線(xiàn)程的初始運(yùn)行地址,即一個(gè)方法指針。
- 3:在內(nèi)核線(xiàn)程的初始運(yùn)行方法中,利用JavaCalls模塊,調(diào)用java線(xiàn)程的run()方法,開(kāi)始java級(jí)別的線(xiàn)程執(zhí)行。
推薦閱讀:
Java編程思想之多線(xiàn)程(三)
作者:半棧工程師
鏈接:https://www.jianshu.com/p/0dd2c5e2d63b
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
總結(jié)
以上是生活随笔為你收集整理的Java并发编程—Thread类的start()方法是如何启动一个线程的?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java并发编程—什么是线程?
- 下一篇: Java集合—哈希(hash)表