join()用于让当前线程等待目标线程执行完毕,本质是同步等待而非调度控制;必须在目标线程start()后调用,否则抛illegalthreadstateexception;支持超时版本避免无限阻塞。

Java 中用 join() 实现两个线程的顺序依赖执行,核心是让主线程(或某个线程)等待另一个线程执行完毕后再继续。这不是“强制线程按序启动”,而是通过阻塞当前线程,确保目标线程先完成。
join 的本质是等待而非调度
join() 方法会让调用它的线程暂停执行,直到被调用的线程结束(即 run() 方法返回)。它不改变线程的启动顺序,也不干预 CPU 调度,只是添加同步等待逻辑。
- 调用
t1.join()后,当前线程会进入WAITING状态,直到t1死亡 - 如果
t1已经结束,join()立即返回,不阻塞 - 必须在
t1.start()之后调用join(),否则会抛出IllegalThreadStateException
典型写法:主线程控制 t1 → t2 顺序执行
假设希望线程 A 执行完后,再启动并等线程 B 执行完:
Thread t1 = new Thread(() -> {
System.out.println("t1 开始");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("t1 结束");
});
Thread t2 = new Thread(() -> {
System.out.println("t2 开始");
try { Thread.sleep(500); } catch (InterruptedException e) {}
System.out.println("t2 结束");
});
t1.start();
t1.join(); // 主线程等 t1 完成
t2.start();
t2.join(); // 主线程等 t2 完成
System.out.println("全部完成");
输出严格为:
t1 开始 → t1 结束 → t2 开始 → t2 结束 → 全部完成
注意 join 的调用位置和对象
谁调用 join(),谁就被阻塞;对哪个线程调用,就等哪个线程。常见误区:
- 错误:在
t1的run()里调用t2.join()—— 这会让t1等t2,但t2可能还没 start,导致死锁风险 - 正确:由共同的“协调者”(如主线程)按需调用
join(),确保被等待线程已启动 - 若想让 t1 启动后、t2 启动前等 t1 结束,必须把
t1.join()放在t2.start()之前
可选:带超时的 join 避免无限等待
使用 join(long millis) 或 join(long millis, int nanos) 设置最长等待时间:
- 若目标线程在超时前结束,
join()返回,继续执行 - 若超时仍未结束,当前线程恢复执行,不抛异常
- 适合有响应时限的场景,比如 UI 线程等待后台任务,但不能卡死
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











