You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中等待指定线程执行完成后再启动其他线程?

解决Java主线程等待批量子线程完成后再执行后续逻辑的问题

这个问题其实是Java线程同步里非常常见的场景——让主线程等一批工作线程全部干完再继续往下走。咱们一步步来解决它:

核心问题分析

你当前的代码只是创建了Cls1的实例,但如果Cls1的构造方法里启动了线程(比如new Thread(this).start()),主线程会直接进入loop2,完全不会等待这些子线程执行完毕。关键原因是你没有保存这些子线程的引用,也就没法主动控制主线程等待它们。


方案一:使用Thread.join()(基础版)

这是最直接的解决方案:保存每个启动的Cls1线程引用,然后让主线程逐个调用这些线程的join()方法,直到所有线程都执行完毕。

完整代码示例

首先补全你的Cls1和Cls2类(假设它们的run()方法里有模拟工作的逻辑):

public class Cls1 implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("Cls1线程 [" + Thread.currentThread().getName() + "] 启动");
            // 模拟业务逻辑,比如休眠1秒
            Thread.sleep(1000);
            System.out.println("Cls1线程 [" + Thread.currentThread().getName() + "] 执行完成");
        } catch (InterruptedException e) {
            // 正确处理中断,恢复中断状态
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

public class Cls2 implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("Cls2线程 [" + Thread.currentThread().getName() + "] 启动");
            Thread.sleep(1000);
            System.out.println("Cls2线程 [" + Thread.currentThread().getName() + "] 执行完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

然后修改Master类,添加线程引用存储和等待逻辑:

import java.util.ArrayList;
import java.util.List;

public class Master {
    public static void main(String[] args) {
        // 用来存储loop1中启动的所有线程
        List<Thread> cls1Threads = new ArrayList<>();

        // loop1:创建并启动Cls1线程
        for (int i = 1; i <= 2; i++) {
            Cls1 c1 = new Cls1();
            Thread thread = new Thread(c1, "Cls1-" + i);
            thread.start();
            cls1Threads.add(thread);
        }

        // 等待所有Cls1线程执行完毕
        System.out.println("主线程开始等待Cls1线程完成...");
        for (Thread thread : cls1Threads) {
            try {
                // 调用join()后,主线程会阻塞,直到当前子线程执行完成
                thread.join();
            } catch (InterruptedException e) {
                // 如果主线程被中断,恢复中断状态并退出
                Thread.currentThread().interrupt();
                System.err.println("主线程被中断,无法继续等待子线程");
                e.printStackTrace();
                return;
            }
        }

        // 所有Cls1线程完成后,才会执行到这里
        System.out.println("=== 所有Cls1线程执行完毕,开始启动Cls2线程 ===");

        // loop2:创建并启动Cls2线程
        for (int j = 1; j <= 2; j++) {
            Cls2 c2 = new Cls2();
            Thread thread = new Thread(c2, "Cls2-" + j);
            thread.start();
        }
    }
}

关键知识点

  • 线程引用存储:必须用集合保存每个启动的线程对象,否则后续无法调用join()方法控制它。
  • join()方法:调用thread.join()时,当前线程(这里是主线程)会进入阻塞状态,直到目标线程执行完毕才会继续往下走。
  • 中断处理:join()会抛出InterruptedException,需要正确处理——恢复当前线程的中断状态,避免后续逻辑出错。

方案二:使用ExecutorService(进阶版,推荐)

如果你的线程数量较多,或者需要更灵活的线程管理(比如超时控制、线程复用),推荐使用JDK提供的线程池框架ExecutorService。

完整代码示例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Master {
    public static void main(String[] args) {
        // 创建固定大小的线程池,大小和loop1的线程数一致
        ExecutorService cls1Executor = Executors.newFixedThreadPool(2);

        // loop1:提交Cls1任务到线程池
        for (int i = 1; i <= 2; i++) {
            cls1Executor.submit(new Cls1());
        }

        // 关闭线程池:不再接受新任务,但会等待已提交的任务完成
        cls1Executor.shutdown();
        try {
            // 等待所有任务完成,设置超时时间(比如10秒)
            System.out.println("主线程开始等待Cls1线程完成...");
            if (!cls1Executor.awaitTermination(10, TimeUnit.SECONDS)) {
                // 如果超时,强制关闭剩余线程
                cls1Executor.shutdownNow();
                System.err.println("Cls1线程执行超时,强制关闭剩余线程");
            }
        } catch (InterruptedException e) {
            cls1Executor.shutdownNow();
            Thread.currentThread().interrupt();
            e.printStackTrace();
            return;
        }

        System.out.println("=== 所有Cls1线程执行完毕,开始启动Cls2线程 ===");

        // loop2:用另一个线程池管理Cls2任务
        ExecutorService cls2Executor = Executors.newFixedThreadPool(2);
        for (int j = 1; j <= 2; j++) {
            cls2Executor.submit(new Cls2());
        }
        cls2Executor.shutdown();
    }
}

优势说明

  • 线程资源管理:线程池可以复用线程,避免频繁创建销毁线程带来的性能开销。
  • 超时控制:awaitTermination()可以设置等待超时时间,防止主线程无限期等待。
  • 更简洁的API:无需手动管理线程引用,线程池会自动处理任务的提交和执行。

内容的提问来源于stack exchange,提问作者frianH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:27:25