thread.join 的含義是當(dāng)前線程需要等待 previousThread 線程終止之后才從previousThread.join 返回少辣。簡(jiǎn)單來(lái)說(shuō)肉盹,就是上一個(gè)線程還沒(méi)有執(zhí)行完之前,當(dāng)前線程會(huì)一直阻塞在 join 方法處隘弊。
join 為什么阻塞的是主線程呢?
先來(lái)看看 Thread.join 方法做了什么事情。
public class Thread implements Runnable {
...
public final void join() throws InterruptedException {
join(0);
}
...
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
...
}
從 join 方法的源碼來(lái)看抓韩,join 方法的本質(zhì)調(diào)用的是 Object 中的 wait 方法實(shí)現(xiàn)線程的阻塞,wait 方法的實(shí)現(xiàn)原理我們?cè)诤罄m(xù)的文章再說(shuō)詳細(xì)闡述鬓长。但是我們需要知道的是谒拴,調(diào)用 wait 方法必須要獲取鎖,所以 join 方法是被 synchronized 修飾的涉波,synchronized 修飾在方法層面相當(dāng)于 synchronized(this)英上,this 就是previousThread 本身的實(shí)例。
有很多人不理解 join 為什么阻塞的是主線程呢?
不理解的原因是阻塞主線程的方法是放在 previousThread 這個(gè)實(shí)例作用啤覆,讓大家誤以為應(yīng)該阻塞 previousThread 線程苍日。實(shí)際上主線程會(huì)持有 previousThread 這個(gè)對(duì)象的鎖,然后調(diào)用 wait 方法去阻塞窗声,而這個(gè)方法的調(diào)用者是在 主線程 中的相恃。所以造成主線程阻塞。
為什么 previousThread 線程執(zhí)行完畢就能夠喚醒主線程呢笨觅?是在什么時(shí)候喚醒的拦耐?
通過(guò) wait方 法阻塞的線程,需要通過(guò) notify 或者 notifyall 來(lái)喚醒见剩。所以在線程執(zhí)行完畢以后會(huì)有一個(gè)喚醒的操作杀糯,只是我們不需要關(guān)心。
Thread.join 其實(shí)底層是通過(guò) wait/notifyall 來(lái)實(shí)現(xiàn)線程的通信達(dá)到線程阻塞的目的苍苞;當(dāng)線程執(zhí)行結(jié)束以后固翰,會(huì)觸發(fā)兩個(gè)事情,第一個(gè)是設(shè)置 native 線程對(duì)象為null、第二個(gè)是通過(guò) notifyall 方法骂际,讓等待在 previousThread 對(duì)象鎖上的 wait 方法被喚醒疗琉。
什么時(shí)候會(huì)使用 Thread.join?
在實(shí)際應(yīng)用開(kāi)發(fā)中,我們很少會(huì)使用 thread.join歉铝。在實(shí)際使用過(guò)程中没炒,我們可以通過(guò) join 方法來(lái)等待線程執(zhí)行的結(jié)果,其實(shí)有點(diǎn)類似 future/callable 的功能犯戏。
我們通過(guò)以下偽代碼來(lái)說(shuō)明 join 的使用場(chǎng)景:
public void joinDemo(){
....
Thread t = new Thread(payService);
t.start();
....
insertData();
//后續(xù)的處理送火,需要依賴 t 線程的執(zhí)行結(jié)果,可以在這里調(diào)用 join 方法等待 t 線程執(zhí)行結(jié)束
t.join();
}