Thread.join()底层通过在目标Thread对象上调用wait()实现等待,JVM在线程终止时自动调用notifyAll()唤醒所有等待线程,确保安全高效;带超时的join基于wait(timeout),超时后直接返回而不抛异常。

Java 中 Thread.join() 的底层逻辑本质上是通过 wait() 实现线程等待,但它不是直接调用 wait(),而是借助目标线程对象自身的监视器(monitor)来完成阻塞与唤醒。
join 方法本质是“在目标线程对象上 wait”
join() 并非在线程实例内部维护状态,而是同步地对目标 Thread 对象加锁,并在其上调用 wait()。因为每个 Thread 对象都是普通 Java 对象,天然具备 monitor 锁和等待队列。
- 调用
thread.join()时,当前线程会尝试获取thread对象的锁 - 获取成功后,检查目标线程是否还存活(
isAlive()) - 若存活,则调用
thread.wait(),使当前线程进入该对象的等待队列并释放锁 - 当目标线程执行结束(JVM 在其终止时自动调用
notifyAll()),所有在该对象上等待的线程被唤醒
JVM 在线程终止时自动 notifyAll
Java 线程正常结束(或异常终止)后,JVM 会负责清理,并在该 Thread 对象上调用 notifyAll() —— 这是 JVM 层面的保障,开发者不可见但必须依赖。
- 这个通知动作由 JVM 在
thread.run()返回后触发,不依赖用户代码 - 因此多个线程调用同一个线程的
join(),都能被正确唤醒 - 注意:
wait()被唤醒后仍需重新竞争锁,之后再检查线程是否真的结束了(防止虚假唤醒)
带超时的 join 是如何工作的
join(long millis) 或 join(long millis, int nanos) 底层调用的是 wait(timeout),即带超时的等待。
立即学习“Java免费学习笔记(深入)”;
- 超时时间到达后,即使目标线程未结束,
join()也会返回 - 此时不会抛异常,只是退出等待逻辑,调用方需自行判断线程状态
- 超时等待同样基于目标线程对象的 monitor,语义一致,只是多了定时机制
为什么不能用其他对象替代 wait?
join() 必须绑定到目标线程对象本身,是因为只有 JVM 知道何时该线程真正结束,并能准确在对应对象上调用 notifyAll()。
- 如果手动创建一个锁对象并
wait(),JVM 不会为你notify,无法自动唤醒 - 试图用
while(!t.isAlive()) Thread.sleep(1)是忙等,浪费 CPU,且不精确 -
join()的设计把“等待条件”和“通知时机”完全交给 JVM 管理,安全且高效
不复杂但容易忽略:join() 的等待对象是线程实例本身,而唤醒动作由 JVM 隐式完成,这是它可靠性的根本所在。


















