日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发编程—Thread类的start()方法是如何启动一个线程的?

發布時間:2024/4/15 java 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发编程—Thread类的start()方法是如何启动一个线程的? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

一:Java線程介紹

二:Java線程入口分析

三:Java線程的創建

四:總結


周末抽了點時間,研究了下HotSpot是如何創建Java線程的,順便總結一下。文中引用的源碼里刪除很多細節,只保留了主要過程,感興趣的同學可以自己去深入研究。能力有限,錯誤地方懇請指正。

一:Java線程介紹

做Java開發或者Android的同學,肯定對Java線程很熟悉。之前在閱讀一些JVM相關的書時,了解到Java線程其實是映射到操作系統的內核線程上的,所以Java線程基本上也就是操作系統在進行管理。在Linux中,線程和進程用的是同一個結構體進行描述的,只不過進程擁有自己獨立的地址空間,而同一個進程的線程之間是共享資源的。

二:Java線程入口分析

想要啟動一個Java線程,主要有兩種方法,第一種可以實現一個繼承自Thread的子類,重寫run();第二種可以實現一個Runnable,交給Thread執行。這兩種方法都很簡便,我們可以根據自己的業務需求選擇,但是無論選擇哪種方法,都需要調用Thread.start(),才能真正的啟動一個異步線程進行工作,筆者剛上大學接觸Java時,曾經天真的以為調用Thread.run()就行了,其實直接調用run(),JVM并不會去創建一個線程,run()只會工作在原有線程,和調用普通對象的方法沒有任何區別。

上面提到了要想啟動線程,必須要調用Thread.start(),那么這個方法便是我們研究的入口了。

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();

通過上面的代碼可以得知,start()方法中做了一些線程狀態判斷等工作,但是真正啟動Java線程的地方是調用了start0(),start0()是一個Native方法。start0()是何處實現的呢?我們先來看看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}, };

上面定義了一個數組,數組中存放的為JNINativeMethod類型的結構體變量,JNINativeMethod定義在jni.h中:

typedef struct {char *name;char *signature;void *fnPtr; } JNINativeMethod;

JNINativeMethod主要是進行一個jni方法的映射關系,將native方法和真正的實現方法進行綁定。那么具體是何時進行綁定的呢?java層的Thread在類的靜態初始化塊中,調用了registerNatives()方法:

private static native void registerNatives();static {registerNatives();}

我們來看看registerNatives()對應的Jni方法:

JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) {(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); }

可見此時會將上述數組methods中的方法映射關系進行注冊。注冊好了后,我們再來看看上面定義的數組,這里面將Java線程中的很多native方法與實現其功能的方法指針進行了綁定,比如start0()就和JVM_StartThread進行了綁定。所以我們想研究start0(),就直接看看JVM_StartThread指針所指向的方法即可。

三:Java線程的創建

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

源碼中本來有很多英文注釋,我這里先給刪了,下面來一步步分析下上面的代碼吧。

1:判斷Java線程是否已經啟動,如果已經啟動過,則會拋異常。

if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {throw_illegal_thread_state = true;}

2: 如果第一步判斷中,Java線程沒有啟動過,則會開始創建Java線程。

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線程的創建過程主要就在JavaThread的構造函數中:

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) 便開始真正的創建Java線程對應的內核線程。

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

上面這個方法主要就是利用pthread_create()來創建線程。其中第三個參數thread_native_entry便是新起的線程運行的初始地址,其為定義在os_bsd.cpp中的一個方法指針,而第四個參數thread即thread_native_entry的參數:

static void *thread_native_entry(Thread *thread) {......thread->run();......return 0; }

新線程創建后就會從thread_native_entry()開始運行,thread_native_entry()中調用了thread->run():

// thread.cpp void JavaThread::run() {......thread_main_inner(); }

此方法最后調用了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; }

我們重點關注下this->entry_point()(this, this),entry_point()返回的其實就是在 new JavaThread(&thread_entry, sz) 時傳入的thread_entry。這里就相當于調用了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); }

哈哈,這里見到了一個老朋友:JavaCalls。JavaCalls模塊是用來調用Java方法的。不了解的朋友可以直接看之前寫的文章《JVM方法執行的來龍去脈》。我們來看看這里調用JavaCalls::call_virtual()傳入的一些參數:

  • obj:Java線程對象;
  • KlassHandle(THREAD, SystemDictionary::Thread_klass()):Java線程類,記錄在SystemDictionary中,即java_lang_Thread;
  • vmSymbols::run_method_name():即"run";
  • vmSymbols::void_method_signature():即"()V";

經過上面的分析,這里其實就是開始調用Java線程對象的run()方法。

3:開始執行所創建的內核線程,即從第二步所說的thread_entry處開始執行。

Thread::start(native_thread);

四:總結

到這里Java線程就已經真正的運行起來了,總結下上面的過程:

  • 1:調用Java線程start()方法,通過jni方式,調用到JVM層。
  • 2:JVM通過pthread_create()創建一個系統內核線程,并指定內核線程的初始運行地址,即一個方法指針。
  • 3:在內核線程的初始運行方法中,利用JavaCalls模塊,調用java線程的run()方法,開始java級別的線程執行。

推薦閱讀:

Java編程思想之多線程(三)

作者:半棧工程師
鏈接:https://www.jianshu.com/p/0dd2c5e2d63b
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

總結

以上是生活随笔為你收集整理的Java并发编程—Thread类的start()方法是如何启动一个线程的?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。