在 Java 面试中,“如何让主线程等待所有子线程执行完毕后再继续?” 是一个高频多线程问题。下面给出 4 种主流实现方式 + 代码示例,覆盖从基础到进阶,面试时按需选择即可。
public class ThreadJoinDemo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> System.out.println("T1"));
Thread t2 = new Thread(() -> System.out.println("T2"));
t1.start();
t2.start();
t1.join(); // 主线程等待 t1 完成
t2.join(); // 主线程等待 t2 完成
System.out.println("主线程继续执行...");
}
}
关键点:
join()
会让当前线程(主线程)阻塞,直到目标线程结束。import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int threadCount = 3;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 完成");
latch.countDown(); // 计数器减1
}).start();
}
latch.await(); // 主线程等待计数器归零
System.out.println("所有子线程完成,主线程继续...");
}
}
适用场景:
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("所有子线程到达栅栏,主线程继续...");
});
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 到达");
try {
barrier.await(); // 等待其他线程
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
特点:
Cyclic
),适合 多次同步 的场景。import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
pool.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 完成");
return "OK";
});
}
pool.shutdown(); // 关闭线程池(不再接受新任务)
pool.awaitTermination(1, TimeUnit.MINUTES); // 等待所有任务完成
System.out.println("所有子线程完成,主线程继续...");
}
}
优势:
“如果子线程需要返回结果,我会用
ExecutorService
提交Callable
任务,通过Future.get()
阻塞获取结果;如果只是等待完成,CountDownLatch
或CompletableFuture.allOf()
会更优雅。”
方法 | 适用场景 | 是否可复用 | 是否阻塞主线程 |
---|---|---|---|
Thread.join() | 简单线程 | 否 | 是 |
CountDownLatch | 固定数量线程同步 | 否 | 是 |
CyclicBarrier | 多次同步或分组协作 | 是 | 否(主线程可继续) |
ExecutorService | 线程池批量任务 | 是 | 是(awaitTermination) |
面试反问:
“如果子线程异常退出,join()
和 CountDownLatch
会怎么处理?”
(可以答:需手动检查线程状态或使用 Future
捕获异常)
掌握这 4 种方式,面试稳了!