在工作中,挺少遇到join關鍵字,但很多多線程資料和麪試過程中,初中級開發工程師總會遇到join。
今天一起學習下join。
join的作用:等待指定的時間(當爲0時,一直等待),直到這個線程執行結束。
先看join方法的定義,join是java.lang.Thread的一個普通方法。
package java.lang;
// Thread竟然實現了Runnable接口,之前好像注意到過,但是沒在意。
// 根據構造方法和run()方法可以看出,本質還是執行的Runnable的實現。
public class Thread implements Runnable {
…
public final void join() throws InterruptedException {
join(0);
}
…
}
演示代碼
public class ThreadLearnJoin {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "睡覺");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "起牀");
}
}, "sub-thread");
t.start();
System.out.println(Thread.currentThread().getName() + "線程join前");
t.join();
System.out.println(Thread.currentThread().getName() + "線程join後");
}
}
執行結果
main線程join前
sub-thread睡覺
sub-thread起牀
main線程join後
源碼分析
繼續分析join(0)的代碼,首先需要注意的是synchronized關鍵字,其次是isAlive()和wait(0)。在演示代碼中,主線程調用名稱爲sub-thread的子線程t的join()方法。
主線程先獲取t對象上的鎖,並且當t爲Alive狀態時,繼續調用t的wait(0)方法。
由於wait()方法是Object的方法,跟子線程t並沒有關係,wait()會釋放t對象上的鎖,並阻塞當前main線程。
這裏也就隱藏了一個點:t線程調用start()後,進入執行狀態,運行run()方法中的代碼,和t對象上的鎖並沒有任何關係。
run方法並沒有進入synchronized的同步區。
public final synchronized void join(long millis) throws InterruptedException {
…
if (millis == 0) {
while (isAlive()) {
wait(0);
}
}
…
}
參考資料
- Java多線程系列--“基礎篇”08之 join()
以下是Java多線程系列--“基礎篇”08之 join()的評論,可以幫助大家理解記憶。
問題:雖然s.join()被調用的地方是發生在“Father主線程”中,但是s.join()是通過“子線程s”去調用的join()。
那麼,join()方法中的isAlive()應該是判斷“子線程s”是不是Alive狀態;對應的wait(0)也應該是“讓子線程s”等待纔對。
但如果是這樣的話,s.join()的作用怎麼可能是“讓主線程等待,直到子線程s完成爲止”呢,應該是讓"子線程等待纔對(因爲調用子線程對象s的wait方法嘛)"?
答案:wait()的作用是讓“當前線程”等待,而這裏的“當前線程”是指當前在CPU上運行的線程。所以,雖然是調用子線程的wait()方法,但是它是通過“主線程”去調用的;
所以,休眠的是主線程,而不是“子線程”!
這個這麼講的不清楚吧?
調用wait()方法 應該是當前線程持有的對象調用wait() 讓線程等,並釋放對象鎖。
在主線程中調用了s.join() join()方法用synchronized修飾了,也就是說當前主線程已經持有了s的鎖 這個調用s這個對象wait()方法 讓主線程等待並釋放s對象鎖。