在實際開發中,異步任務協調是常見場景:主線程啟動子線程執行耗時操作,需等待子線程完成後繼續執行。Thread.join() 正是解決此類「等待線程終止」需求的核心 API。本文將從源碼、使用陷阱、實戰示例三個維度深度解析。
public final void join() throws InterruptedException {
join(0); // 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;
}
}
}
關鍵點解讀:
delay = millis - now 精確控制超時public final synchronized void join(long millis, int nanos) throws InterruptedException {
if (nanos >= 500000 || (nanos != 0 && millis == 0)) millis++; // 納秒進位
join(millis); // 最终委託毫秒版本
}
public class JoinDemo {
public static void main(String[] args) {
ThreadMain main = new ThreadMain();
main.start(); // 啟動主線程(實為主線程創建的子線程)
}
}
class ThreadMain extends Thread {
ThreadSub sub = new ThreadSub();
public void run() {
System.out.println("ThreadMain starts!");
sub.start(); // 啟動子線程
System.out.println("ThreadMain running before threadsub!");
try {
sub.join(); // 關鍵:等待 sub 結束
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadMain running after threadsub!");
System.out.println("ThreadMain ends!");
}
}
class ThreadSub extends Thread {
public void run() {
System.out.println("ThreadSub starts!");
for (int i = 0; i < 10; i++) {
System.out.println("ThreadSub running: " + (i + 1));
}
System.out.println("ThreadSub ends!");
}
}
ThreadMain starts!
ThreadMain running before threadsub!
ThreadSub starts!
ThreadSub running: 1
...
ThreadSub running: 10
ThreadSub ends!
ThreadMain running after threadsub!
ThreadMain ends!
輸出清晰驗證了 join() 語義:主線程在 sub.join() 處阻塞,直到子線程 run() 方法執行完畢(即線程死亡)才繼續執行後續代碼。
join(5000) 限制最大等待 5 秒,防止死等Thread.join() 基於 wait/notify 機制實現線程等待,是 Java 併發編程中「等待線程終止」的標準方案。掌握其超時語義、中斷處理、啟動順序三大要點,能有效避免併發協調中的常見坑點。