前述
面試過的小伙伴,應(yīng)該都被問到過這個問題。估計被問蒙蒙的不少 :-D
今天我們可以一起來分析一下,文章不長,相信你通過這一篇文章,就可以有很好的掌握了!
一、方法的歸屬
- wait()方法是object類的方法
- join()是Thread的方法。
二、 不正經(jīng)的解釋
-
wait()我要休息一會,我累了
-
join()老子要插隊,都NM給我讓開,都等一等
image
官方一點(diǎn)的說法:
Wait的用法:
當(dāng)一個線程調(diào)用wait的時候,會釋放同步鎖,然后該線程進(jìn)入等待狀態(tài)。其他掛起的線程會競爭這個鎖,得到鎖的繼續(xù)執(zhí)行。
join的用法:
一個線程運(yùn)行中調(diào)用另外線程的JOIN方法,則當(dāng)前線程停止執(zhí)行,一直等到新join進(jìn)來的線程執(zhí)行完畢,才會繼續(xù)執(zhí)行??!
join的測試用例:
一、不加join
public class Main{
public static void main(String[] args) {
System.out.println("Main 線程 開始運(yùn)行!");
Thread t1 = new Thread(){
@Override
public void run(){
System.out.println("t1 開始運(yùn)行!");
System.out.println("t1 結(jié)束運(yùn)行!");
}
};
t1.start();
System.out.println("Main 線程 結(jié)束運(yùn)行!");
}
}
打印結(jié)果為:
Main 線程 開始運(yùn)行!
Main 線程 結(jié)束運(yùn)行!
t1 開始運(yùn)行!
t1 結(jié)束運(yùn)行!
說明主線程執(zhí)行完畢,才執(zhí)行的子線程t1,這個大家都懂!
二、加join
public class Main{
public static void main(String[] args) {
System.out.println("Main 線程 開始運(yùn)行!");
Thread t1 = new Thread(){
@Override
public void run(){
System.out.println("t1 開始運(yùn)行!");
System.out.println("t1 結(jié)束運(yùn)行!");
}
};
try{
t1.start();
t1.join();
}catch(Exception e){
}
System.out.println("Main 線程 結(jié)束運(yùn)行!");
}
}
打印結(jié)果為:
Main 線程 開始運(yùn)行!
t1 開始運(yùn)行!
t1 結(jié)束運(yùn)行!
Main 線程 結(jié)束運(yùn)行!
說明t1線程插隊了,直到t1運(yùn)行完畢,主線程才繼續(xù)運(yùn)行。
所以我們可以先簡單理解為join就是新線程插隊執(zhí)行(當(dāng)前運(yùn)行線程阻塞直到新線程運(yùn)行結(jié)束?。?/p>
接下里我們拋出問題,然后再來認(rèn)真分析join()的原理。
問題1:
上面例子中,我們把join()和start()調(diào)換個順序,會發(fā)現(xiàn)輸出結(jié)果為:
Main 線程 開始運(yùn)行!
Main 線程 結(jié)束運(yùn)行!
t1 開始運(yùn)行!
t1 結(jié)束運(yùn)行!
why??
下面我們來分析源碼Thread.java
public final void join(long millis) throws InterruptedException {
synchronized(lock) {//主線程拿到lock鎖
long base = System.currentTimeMillis();
if (millis == 0) {
while (isAlive()) { //由于該線程已經(jīng)start(),所以視為alive
lock.wait(0);
//主線程釋放鎖,進(jìn)入無限期的等待狀態(tài)。
//直到子線程完成run,釋放鎖,然后主線程會重新拿到鎖頭繼續(xù)運(yùn)行
//拿到鎖之后,isAlive()不成立了,所以退出while循環(huán)??!
}
} else {
}
}
}
}
調(diào)用join()時, 默認(rèn)millis為0。
如上代碼,如果沒有先執(zhí)行start()直接執(zhí)行join,則isAlive()返回為false,則主線程不會堵塞進(jìn)入wait(0),這就是為什么一定要先start()然后再join()的原因所在。
問題2:
為什么join()可以阻塞主線程,直到子線程執(zhí)行完畢??
同樣看上面代碼:
- 主線程進(jìn)入join()方法
- 主線程拿到子線程的lock鎖
- 進(jìn)入同步代碼快
- while (isAlive()) 成立,因為先調(diào)用了start()方法
- 調(diào)用 lock.wait(0), 主線程釋放鎖,進(jìn)入wait狀態(tài)
- 子線程開始執(zhí)行,執(zhí)行結(jié)束會調(diào)用lock.notifyAll(),通知主線程獲得鎖。
- 主線程重新啟動, while (isAlive()) 已經(jīng)不成立(由于子線程不再是alive狀態(tài))
- 主線程繼續(xù)往下運(yùn)行。
其中倒數(shù)第三部,是在jdk的Thread.cpp里完成的,可以先不做研究!!

相信到此,你大致了解了一下機(jī)理。
總結(jié)與綜述
- wait是object類的方法
- join是Thread類的方法
- Wait的用法:當(dāng)一個線程調(diào)用wait的時候,會釋放同步鎖,然后該線程進(jìn)入等待狀態(tài)。其他掛起的線程會競爭這個鎖,得到鎖的繼續(xù)執(zhí)行。
- join的用法:一個線程A運(yùn)行中調(diào)用線程B.join()方法,則A線程停止執(zhí)行,一直等到B線程執(zhí)行完畢,A線程才會繼續(xù)執(zhí)行!!
- join方法的實現(xiàn),利用了wait()和notifyAll()方法。
搞定~~~ 有問題歡迎一起交流

