Thread類的join方法,Java官方文檔的解釋是:Waits for this thread to die.(等待線程死亡)。也就是程序會等待調用join方法的線程運行完,再執行當前線程,但不影響除這2個線程之外的線程的運行。這樣簡單的解釋可能很多同學並不是很理解,下面將詳細地解釋一些join方法。
首先我們先看一下join方法的代碼實現(JDK8):
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) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
我們平時調用的無參的join方法最終會調用到這個帶一個參數的join方法,參數是0。看關鍵的第一個while循環的代碼,while會判斷調用此方法的線程是否是alive狀態(如果線程沒有運行完都是alive狀態,包括運行和阻塞),如果線程已經運行完,則方法結束,什麼也不做。如果線程沒有運行完,則調用調用此join方法線程的wait方法。這裏需要着重注意,此join方法是synchronized方法,也就是調用此方法的時,會在調用此方法的對象上加鎖。假設在B線程中調用的A線程對象的join方法,那麼就會在A線程對象上加鎖,這時調用wait方法時,注意這裏是在B線程中調用的A線程對象的wait方法,因此當前線程(B線程)就會處於等待狀態,並釋放A線程對象上的這把鎖。問題在於,我們在學習線程時知道,調用wait方法等待的線程需要等待其他線程調用notify或notifyAll方法並再此獲得這把鎖才能繼續運行,那爲什麼B線程wait以後,A線程執行完,B線程就又可以繼續執行了呢?原因在於,我們的鎖是加在A線程對象上的,當A線程運行結束之後,notify方法會被線程子系統調用(The notify() for this is handled by the Thread subsystem.),也就是說當線程運行結束後,線程對象上的notify方法會被調用。因此當A線程執行完,A線程對象就會調用notify方法,此時B線程就會被喚醒從而繼續運行。
下面我們通過具體的代碼來看一下:
public class Main {
public static void main(String[] args) {
final Thread threadA = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + " is running..." + i);
}
}, "A");
threadA.start();
final Thread threadB = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + " is running..." + i);
if (i == 500) {
try {
threadA.join(); //調用threadA的join方法,相當於執行了下面註釋的代碼
// synchronized (threadA) {
// while (threadA.isAlive()) {
// threadA.wait(0);
// }
// }
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "B");
threadB.start();
}
}
我們創建並啓動了A和B兩個線程,在每個線程中分別打印當前線程的名字和循環次數,在B線程循環到500次時,調用A線程的join方法,注意,此時相當於相當於執行了join下面的註釋的代碼,然後B線程會等待,直到A線程運行結束以後,B線程纔會繼續運行。但由於join方法是在B線程中調用的,因此它並不會影響除A、B線程之外的其他線程的運行。