首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 並發編程實戰(六):Thread.join() 等待線程終止完整指南

     編輯:Java 相關知識

應用場景

主線程需要等待一組初始化線程(數據源加載、網絡連接建立、配置拉取)全部完成,才能繼續業務邏輯。

基礎用法


Thread t1 = new Thread(() -> { loadDataSources(); }, "DataSource-Loader");
Thread t2 = new Thread(() -> { initConnections(); }, "Connection-Initializer");

t1.start();
t2.start();

try {
    t1.join();  // 等待 t1 結束
    t2.join();  // 等待 t2 結束
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // 保留中斷狀態
    log.error("等待初始化線程被中斷", e);
}

System.out.println("所有初始化完成,啟動業務服務...");

重載版本與超時控制

超時等待模式


const long STARTUP_TIMEOUT_MS = 30_000;
long start = System.currentTimeMillis();

for (Thread t : initThreads) {
    long remaining = STARTUP_TIMEOUT_MS - (System.currentTimeMillis() - start);
    if (remaining <= 0) throw new IllegalStateException("啟動超時");
    t.join(remaining);
}

原理剖析


// Thread.join() 簡化源碼 (JDK 17)
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();
    if (millis == 0) {
        while (isAlive()) wait(0);  // 關鍵:wait() 釋放鎖,進入 WAITING
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) break;
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

本質:調用者持有線程對象鎖,調用 wait() 進入等待集合;目標線程終止時 JVM 自動 notify_all()。

最佳實踐

  1. 捕獲 InterruptedException 後恢復中斷狀態:Thread.currentThread().interrupt()
  2. 避免死等:關鍵初始化用無限 join,非關鍵用超時 join
  3. 線程命名:便於排查堆棧、日誌關聯
  4. 考慮 CountDownLatch / CompletableFuture:更靈活的多線程協作工具

對比:join vs CountDownLatch

方法行為典型用途
join()無限等待核心初始化、必須成功
join(long millis)最多等待毫秒數非核心初始化、設定啟動超時
join(long millis, int nanos)納秒級補充極高精度場景(極少用)
維度Thread.join()CountDownLatch
等待對象Thread 實例計數器
靈活性綁定線程生命周期任意任務、可復用
中斷響應拋出 InterruptedException同
適用場景等待特定線程結束等待 N 個任務完成、可分離線程

完整示例:啟動器模式


public class AppBootstrap {
    private static final Logger log = LoggerFactory.getLogger(AppBootstrap.class);

    public static void main(String[] args) {
        List initThreads = List.of(
            new Thread(() -> { sleep(2000); log.info("數據源就緒"); }, "DS"),
            new Thread(() -> { sleep(3000); log.info("緩存預熱完成"); }, "Cache"),
            new Thread(() -> { sleep(1500); log.info("配置中心拉取完成"); }, "Config")
        );

        initThreads.forEach(Thread::start);

        try {
            for (Thread t : initThreads) t.join(10_000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("啟動被中斷", e);
            System.exit(1);
        }

        log.info("=== 應用啟動成功 ===");
        // 啟動 Web 容器、gRPC 服務等
    }
    private static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved