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()
。