您現在的位置是:首頁 > 綜藝首頁綜藝
java多執行緒之Thread建構函式(原始碼分析)
一個類的建構函式最多有幾個
在上一篇文章中對執行緒狀態生命週期和常見的執行緒api進行了一個講解。這篇文章開始著重對其構造方法進行一個說明,也將揭曉為什麼我們呼叫了start方法就能啟動一個執行緒。
一、守護執行緒和非守護執行緒
我們獲取執行緒的id的時候會發現每次都不是0,這是因為在java虛擬機器執行一個執行緒的時候會預設啟動一些其他的執行緒,來為我們的執行緒服務。預設建立的和我們自己建立的執行緒是有區分的。這就要區分守護執行緒和非守護執行緒了。
1、什麼是守護執行緒和非守護執行緒?
預設啟動的這些執行緒就是守護執行緒,他專門處理一些後臺的工作。比如說垃圾回收等。非守護執行緒就是我們自己建立的這些執行緒。官方文件指出,當java虛擬機器中沒有非守護執行緒了,預設執行緒也會退出。舉個例子就能明白:
守護執行緒就像飯店裡面的服務員,非守護執行緒就像是顧客,顧客沒有了,那麼服務員也沒有存在的必要了。
2、程式碼演示
我們透過程式碼來演示一下他們的作用,
在這裡主要有兩個執行緒一個是main執行緒,第二個就是自己建立的thread。執行之後很明顯程式會無線的執行下去,因為thread是非守護執行緒。即使是main執行緒執行結束了thread也會執行。現在我們把thread設定為守護執行緒就不一樣了。
在執行一遍,我們會發現程式正常的退出了,這是因為我們把thread設定成了守護執行緒,你想想看main執行緒和thread都變成了服務員,現在沒有顧客了,於是這些守護執行緒到店裡轉悠一圈就走了。
3、守護執行緒應用場景?
你瞭解了守護執行緒的特點之後,就可以運用這個原理做一些意想不到的事,比如說在退出jvm的時候也想讓一些執行緒跟著退出,就可以把他設定為守護執行緒。
對這個基本的概念瞭解了之後我們再來看看執行緒的建構函式。
二、執行緒的建構函式
1、建構函式
執行緒Thread得建構函式一共有8個,
在這裡我們接觸到了一個新的類ThreadGroup。它代表的含義就是一個執行緒所屬的執行緒組。在上面我們可以看到在例項化一個執行緒時候,既可以指定執行緒所屬的執行緒組,也可以宣告其runnable介面。下面我們分析一下這個執行緒組ThreadGroup。
他們倆的關係可以這樣表示:
在上面說我們能夠指定執行緒所在的執行緒組,下面我們就程式碼演示一下。
這就是其基本用法,但是如果我們沒有指定執行緒所屬的執行緒組輸出會是什麼結果呢?測試一下:
上面的程式碼的意思是這樣的,執行緒A指定了我們建立的執行緒組,執行緒B預設的執行緒組,maingroup是主執行緒組。根據輸出結果我們會發現,如果一個執行緒沒有指定執行緒組,那麼他就和父親的執行緒組是一樣的。
2、例項化一個執行緒
上面給出了執行緒的八個構造方法,我們可以使用這八個構造方法去例項化一個執行緒,但是底層是如何做的呢?會不會是像普通類那樣例項化的呢?對此我們就需要深入執行緒的原始碼去看看:
我們選用了一個最複雜的構造方法,因為其他構造方法都是其子集,我們可以看到,這裡其實呼叫的是init方法,也就是說真正實現初始化的是在init方法中進行的。我們不妨跟進去看看:
這個init方法裡面還有一層,而且還多出了兩個引數。想要搞清楚我們就需要再跟進去看看:
終於找到了初始化執行緒的方法。我們劃分了五個部分:
(1)第一部分:確保執行緒名字不能為空,
在這裡就不得不提一句執行緒名了,java官方要求我們開發者如果沒有顯示的為執行緒指定一個名字,那麼執行緒將以“Thread-”為字首以數字為字尾,組成執行緒的名字。但是無論如何執行緒都需要有一個名字。
(2)第二部分:指定當前執行緒的執行緒組
裡面的程式碼很明白,也就是說如果g不為空,我們就是用這個g作為當前執行緒的執行緒組,否則的話就使用父類的執行緒組。當然了,中間還要檢查一下許可權問題等等。
(3)第三部分:其他屬性配置
在這裡配置了是否設定為守護執行緒、優先順序、類載入器等。
(4)第四部分:runnable介面配置
指定實現了runnable介面類。
(5)第五部分:設定棧大小
執行緒的棧大小 根據引數傳遞過程可以看出預設大小為零,即使用預設的執行緒棧大小
(6)第六部分:設定執行緒id
執行緒的ID是在nextThreadID方法中指定的。我們可以看看如何指定執行緒的ID的。
可以看到,其實就是threadSeqNumber。
OK,以上就是如何初始化一個執行緒,相信我們都比較清楚了,其他的建構函式只是對這個init方法的引數進行了一些改變而已。但是原理都是一樣的。上篇文章中提到的一個問題還沒有解決,接著往下看。
三、為什麼呼叫start方法就能啟動一個執行緒
為了解決這個問題我們還必須要深入原始碼看一下(jdk1。8):
這些程式碼的意思是什麼呢?首先會判斷執行緒狀態是否異常,然後把當前執行緒在啟動之前加入到執行緒組中,最後呼叫start0方法正式的啟動執行緒。現在關鍵來了,真正啟動執行緒的是這個start0方法,我們不妨再追進去看看:
也就是說真正啟動時native方法啟動的,好像也沒有呼叫run方法,為什麼run方法裡面的內容就被執行了呢。官方文件是這麼解釋的:JNI方法start0內部呼叫了run方法。就是這麼一句話就解釋了上面的這個原因。
上面已經解決了兩個問題,第一個就是建構函式,第二個也理解了為什麼我們呼叫start方法就能啟動一個執行緒而不是run。我們分析原始碼就能知道,執行緒提供的api方法基本上全部是native的。
公眾號:java的架構師技術棧。專注於從小白到架構師系列的文章,回覆關鍵字可獲取各種教程資源和學習路線