如何在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
相关产品推荐
相关产品推荐

