在Java多线程编程中,`wait()` 方法是一个非常重要的同步机制,它通常与 `notify()` 和 `notifyAll()` 一起使用,用于控制线程之间的协作。虽然很多开发者对 `wait()` 的基本用法有所了解,但在实际应用中,如果不理解其背后的原理和使用场景,很容易导致程序出现死锁或逻辑错误。
一、wait() 方法的基本作用
`wait()` 是 `Object` 类中的一个方法,因此所有对象都可以调用该方法。当一个线程调用某个对象的 `wait()` 方法时,它会释放该对象的锁,并进入等待状态,直到其他线程调用该对象的 `notify()` 或 `notifyAll()` 方法来唤醒它。
需要注意的是,调用 `wait()` 必须在同步块或同步方法中进行,否则会抛出 `IllegalMonitorStateException` 异常。
二、wait() 的使用方式
以下是一个简单的示例代码:
```java
public class WaitExample {
public static void main(String[] args) {
final Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程1开始等待");
lock.wait();
System.out.println("线程1被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程2准备唤醒");
lock.notify();
System.out.println("线程2已唤醒");
}
});
t1.start();
t2.start();
}
}
```
在这个例子中,线程1调用 `lock.wait()` 进入等待状态,而线程2在获得锁后调用 `lock.notify()` 唤醒线程1。运行结果大致如下:
```
线程1开始等待
线程2准备唤醒
线程2已唤醒
线程1被唤醒
```
三、wait() 与 sleep() 的区别
很多人容易将 `wait()` 和 `Thread.sleep()` 混淆,但实际上它们有本质的区别:
| 特性 | wait()| sleep()|
|--------------|---------------------------------|------------------------------|
| 所属类 | Object| Thread |
| 是否释放锁 | 是| 否 |
| 唤醒方式 | 通过 notify/notifyAll | 自动唤醒(超时后) |
| 使用场景 | 多线程协作| 线程暂停执行 |
四、常见的错误使用方式
1. 不在同步块中调用 wait()
如果没有在 `synchronized` 块或方法中调用 `wait()`,程序会抛出 `IllegalMonitorStateException`。
2. 忘记调用 notify() 或 notifyAll()
如果没有唤醒等待的线程,程序可能会一直挂起,造成死锁。
3. 误用多个条件变量
在复杂场景中,建议结合 `Condition` 接口(如 `ReentrantLock` 提供的 `newCondition()`)来管理多个等待条件。
五、总结
`wait()` 是 Java 多线程中实现线程间通信的重要工具,合理使用可以提高程序的并发效率和稳定性。但必须注意其使用前提和配合使用的 `notify()` 或 `notifyAll()` 方法,避免因逻辑错误导致线程阻塞或死锁问题。
在实际开发中,推荐结合 `ReentrantLock` 和 `Condition` 来替代传统的 `wait()` 和 `notify()`,以获得更灵活的线程控制能力。