
本文详解在 Java 并发编程中,如何让主线程或任务提交线程可靠地等待前一个子线程(或任务)执行完毕后再继续,重点介绍 Thread.join() 和 Future.get() 两种标准、安全且推荐的同步方式。
本文详解在 java 并发编程中,如何让主线程或任务提交线程可靠地等待前一个子线程(或任务)执行完毕后再继续,重点介绍 `thread.join()` 和 `future.get()` 两种标准、安全且推荐的同步方式。
在多线程协作场景中,常需确保某项任务(如 Asistent)严格串行执行——即当前任务必须等前一个同类型任务彻底结束,才可启动。但直接调用 executor.submit(new Thread(...)) 是错误做法:它不仅冗余包装了 Thread 对象(ExecutorService 本身已负责线程调度),更会导致任务提交与执行完全解耦,无法实现等待逻辑。
✅ 正确思路是:获取任务的执行句柄,并主动阻塞等待其完成。Java 提供两种主流机制:
✅ 方式一:使用 Future.get()(推荐,与 ExecutorService 天然契合)
submit() 方法返回 Future>,其 get() 方法会阻塞当前线程,直至对应任务执行完毕(含正常结束或异常终止):
ExecutorService studenti = Executors.newFixedThreadPool(4);
Future> previousTask = null;
for (int i = 0; i currentTask = studenti.submit(new Asistent());
try {
if (previousTask != null) {
previousTask.get(); // 阻塞直到上一个 Asistent 结束
}
previousTask = currentTask; // 更新为当前任务
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Asistent task failed", e);
}
} else {
// Profesor 可并行执行,无需等待
studenti.submit(new Profesor(barrier));
}
}
⚠️ 注意:Future.get() 在任务抛出异常时会封装为 ExecutionException 抛出,务必捕获处理;若线程被中断,会抛 InterruptedException,建议恢复中断状态。
✅ 方式二:使用 Thread.join()(仅适用于显式创建 Thread 的场景)
若坚持手动管理线程(不推荐),可通过 join() 实现等待:
Thread previousThread = null; for (int i = 0; i <blockquote><p>⚠️ 注意:join() 仅对 Thread 生效,且 isAlive() 检查非必需(join() 对已终止线程立即返回),但需确保 previousThread 不为 null。</p></blockquote><h3>❌ 常见误区纠正</h3>
- 不要在 ExecutorService 中再 new Thread(...):submit(Runnable) 已由线程池调度,嵌套 Thread 会造成资源浪费和控制混乱;
- 避免轮询或 Thread.sleep() 等待:既低效又不可靠;
- CyclicBarrier 不适用于此场景:它用于多个线程互相等待到达同一屏障点,而非“一个线程等待另一个结束”。
总结
| 场景 | 推荐方案 | 关键优势 |
|---|---|---|
| 使用 ExecutorService(最佳实践) | Future.get() | 与线程池无缝集成,支持异常传播,语义清晰 |
| 必须手动管理 Thread 实例 | Thread.join() | 简单直接,但失去线程复用与统一管理能力 |
最终,应优先采用 Future.get() 实现任务级串行依赖,既符合并发设计规范,又保障逻辑健壮性与可维护性。










