文章簡介
很多人對的Thread.join的作用以及實現(xiàn)了解得很少鹦肿,畢竟這個API我們很少使用。這篇文章仍然會結(jié)合使用及原理進行深度分析
內(nèi)容導航
- 的Thread.join的作用
- 的Thread.join的實現(xiàn)原理
- 什么時候會使用的Thread.join
一箩溃、的Thread.join的作用
之前有人問過我一個這樣的面試題
Java的中如何讓多線程按照自己指定的順序執(zhí)行?
這個問題最簡單的回答是通過的Thread.join來實現(xiàn)涣旨,久而久之就讓很多人誤以為的Thread.join是用來保證線程的順序性的歪架。感興趣的可以加入我的學習圈子:142019080免費獲取
下面這段代碼演示了的Thread.join的作用
上面的代碼和蚪,注意部分,大家可以把這行代碼注釋以后看看運行效果攒霹,在沒有加加盟的時候運行的結(jié)果是不確定的。加了加入以后浆洗,運行結(jié)果按照遞增的順序展示出來。previousThread.join
的Thread.join的含義是當前線程需要等待previousThread線程終止之后才從的Thread.join返回伏社。簡單來說,就是線程沒有執(zhí)行完之前摘昌,會一直阻塞在加入方法處。
下面的圖表現(xiàn)了加盟對于線程的作用
二聪黎、的Thread.join的實現(xiàn)原理
線程是如何被阻塞的?又是通過什么方法喚醒的呢?先來看看的Thread.join方法做了什么事情
從加入方法的源碼來看杀赢,加入方法的本質(zhì)調(diào)用的是對象中的等待方法實現(xiàn)線程的阻塞,等待方法的實現(xiàn)原理我們在后續(xù)的文章再說詳細闡述脂崔。但是我們需要知道的是,調(diào)用等方法必須要獲取鎖砌左,所以加入方法是被同步的修飾的脖咐,同步的修飾在方法層面相當于同步(這)汇歹,這就是previousThread本身的實例。
有很多人不理解加入為什么阻塞的是主線程呢产弹?不理解的原因是阻塞主線程的方法是放在previousThread這個實例作用,讓大家誤以為應該阻塞previousThread線程痰哨。實際上主線程會持有previousThread這個對象的鎖,然后調(diào)用等方法去阻塞斤斧,而這個方法的調(diào)用者是在主線程中的早抠。所以造成主線程阻塞撬讽。
第二個問題,為什么previousThread線程執(zhí)行完畢就能夠喚醒住線程呢游昼?或者說是在什么時候喚醒的?
要了解這個問題酱床,我們又得翻JDK的源碼羊赵,但是如果大家對線程有一定的基本了解的話扇谣,通過等方法阻塞的線程,需要通過通知或者notifyAll的來喚醒罐寨。所以在線程執(zhí)行完畢以后會有一個喚醒的操作,只是我們不需要關(guān)心鸯绿。
接下來在熱點的源碼中找到,看看線程退出以后有沒有做相關(guān)的事情來證明我們的猜想。thread.cpp
一下觀察這行代碼上的注釋毒返,喚醒處于等待的線程對象,這個是在線程終止之后做的清理工作拧簸,這個方法的定義代碼片段如下ensure_join(this)
ensure_join方法中,調(diào)用喚醒所有等待thread鎖的線程盆赤,意味著調(diào)用了加入方法被阻塞的主線程會被喚醒;到目前為止,我們基本上對聯(lián)的原理做了一個比較詳細的分析lock.notify_all(thread);
總結(jié)歉眷,的Thread.join其實底層是通過等待/ notifyAll的來實現(xiàn)線程的通信達到線程阻塞的目的;當線程執(zhí)行結(jié)束以后,會觸發(fā)兩個事情汗捡,第一個是設(shè)置本地線程對象為空時,第二個是通過notifyAll的方法扇住,讓等待在previousThread對象鎖上的等待方法被喚醒庸追。
三台囱、什么時候會使用的Thread.join
在實際應用開發(fā)中,我們很少會使用的Thread.join簿训。在實際使用過程中,我們可以通過加入方法來等待線程執(zhí)行的結(jié)果强品,其實有點類似未來/調(diào)用的功能。
我們通過以下偽代碼來說明加入的使用場景
《架構(gòu)師資料》領(lǐng)取方法
上圖中的資料都是我精心錄制視頻的榛,感興趣的可以加入我的學習圈子:142019080免費獲取