Object.wait() 和 Thread.join() 是 Java 中用于线程间协作的两种机制,它们虽然看起来有相似的行为(阻塞线程的执行),但作用和使用场景完全不同。为了更好地理解它们的区别,我们需要深入探讨它们的原理、使用场景和底层机制。
1. Object.wait() 的作用与原理
Object.wait() 是 Java 中用于实现线程 同步 和 线程间通信 的一种机制。它与 notify() 和 notifyAll() 搭配使用,主要用于 等待某个条件的变化。
1.1 使用场景
wait() 通常在 共享资源 的访问中使用,尤其是在多个线程同时操作同一个对象时:
- 当某个线程无法继续执行(通常是因为条件不满足)时,它可以调用
wait()进入 等待状态,直到其他线程修改了该条件并调用notify()或notifyAll()。 wait()必须在 同步块 或 同步方法 中调用,且它会 释放持有的锁,允许其他线程继续执行。
1.2 wait() 的底层原理
wait() 的执行流程可以概括为以下几步:
- 进入同步块:
wait()必须在持有某个对象锁的情况下执行,即需要在synchronized块中调用。 - 释放锁并阻塞:当
wait()被调用时,当前线程会 释放当前对象的锁 并进入 阻塞状态,直到其他线程调用notify()或notifyAll()来唤醒它。 - 唤醒:当某个线程调用了
notify()或notifyAll()后,正在等待该对象锁的线程会被唤醒。 - 重新获取锁:被唤醒的线程在继续执行之前必须 重新获得该对象的锁,只有当它获取到锁后,才会继续执行
wait()之后的代码。
javaCopy codesynchronized (sharedObject) {
while (!condition) {
sharedObject.wait(); // 当前线程进入等待状态,并释放锁
}
// 继续执行其他操作
}
1.3 wait() 与 notify() 的配合
notify():唤醒正在等待该对象锁的 一个线程。notifyAll():唤醒所有正在等待该对象锁的线程。
1.4 关键点
wait()必须在synchronized块中调用,并且调用wait()的对象就是持有的锁对象。wait()调用后,线程会 释放锁 并进入 等待队列,直到被其他线程唤醒。notify()/notifyAll()是用于唤醒等待的线程。
2. Thread.join() 的作用与原理
Thread.join() 是 Java 中用于 线程控制 的一种机制,它用于 等待另一个线程的结束。
2.1 使用场景
join() 通常在需要保证某个线程 执行完毕 后,才继续执行当前线程的情况下使用。例如,主线程可以调用子线程的 join(),让主线程等待子线程执行完毕后再继续执行。
2.2 join() 的底层原理
join() 的执行流程如下:
- 当
join()被调用时,当前线程(例如主线程)会 等待目标线程执行完毕。 - 当前线程会进入 等待状态,直到目标线程执行完毕,即线程的
isAlive()返回false。 - 目标线程执行完毕后,
join()方法会返回,当前线程恢复执行。
javaCopy codeThread t1 = new Thread(() -> {
// 子线程执行的操作
});
t1.start();
try {
t1.join(); // 主线程等待 t1 完成
} catch (InterruptedException e) {
e.printStackTrace();
}
// 主线程继续执行
2.3 join() 的实现
Thread.join() 的底层实现实际上使用了 wait()。具体而言,当一个线程调用 join() 时,当前线程会进入 等待状态,直到目标线程执行完毕时,join() 内部会通过 notify() 唤醒等待的线程。内部调用了线程对象的 wait() 来实现等待,直到目标线程结束。
2.4 关键点
join()是一种用于控制线程执行顺序的方法,调用线程会等待目标线程执行完毕。- 它主要用于线程 结束 时的同步控制。
- 与
wait()不同的是,join()是线程级别的控制,而wait()/notify()是对象锁的控制。
3. Object.wait() 与 Thread.join() 的主要区别
| 特性 | Object.wait() | Thread.join() |
|---|---|---|
| 功能 | 用于线程间的协作与通信,线程等待条件变化 | 用于等待某个线程执行结束 |
| 应用场景 | 需要与 notify() 或 notifyAll() 配合使用 | 线程间的顺序控制,等待线程结束 |
| 调用方式 | 必须在同步块中调用,并且需要持有对象的锁 | 可以在任何地方调用,不需要同步块 |
| 是否释放锁 | 调用后会释放锁,并等待其他线程唤醒 | 不涉及锁的释放,仅等待目标线程结束 |
| 对象级别 or 线程级别 | 对象级别,等待在对象锁上 | 线程级别,等待线程执行完毕 |
| 底层机制 | 调用线程进入 等待队列,直到被 notify() 唤醒 | 当前线程进入 等待状态,直到目标线程结束 |
4. 使用场景对比
4.1 Object.wait() 适合的场景
wait() 适用于 生产者-消费者模型、任务队列 等需要线程间通信的场景。
- 例如,在生产者-消费者问题中,生产者线程在任务队列满时调用
wait(),消费者线程在任务队列空时调用wait(),当条件满足后,双方通过notify()或notifyAll()通信。
4.2 Thread.join() 适合的场景
join() 适用于 线程执行顺序控制。常见的使用场景是:
- 主线程启动若干个子线程进行并行任务,主线程通过
join()方法等待这些子线程执行完毕后,才进行最终的结果汇总或后续操作。
总结
Object.wait():用于线程间通信,等待某个条件的改变,常用于同步块中与notify()配合,涉及对象级别的锁控制。Thread.join():用于线程的顺序控制,当前线程等待目标线程执行完毕后继续执行,常用于主线程与子线程的协调。
了解这两者的差异及其适用场景有助于编写更高效的多线程程序。在需要线程间通信时选择 wait()/notify(),而在需要控制线程执行顺序时选择 join()。