在Java中,wait()
和notify()
方法是用于线程间通信的重要同步机制。它们主要用于协调多个线程对共享资源的访问,确保线程安全。以下是对这两个方法的详细解释和使用场景说明:
1. wait()
方法
定义
wait()
方法使当前线程进入等待状态,直到其他线程调用此对象的notify()
或notifyAll()
方法。当一个线程调用wait()
时,它会释放该对象上的锁,并进入等待队列,直到被唤醒。
使用场景
- 生产者-消费者模型:在生产者-消费者模型中,生产者线程生产数据后调用
notify()
通知消费者线程可以消费数据;消费者线程在没有数据可消费时调用wait()
进入等待状态。 - 任务调度:在某些任务调度系统中,某些任务可能需要等待特定条件满足后再继续执行,这时可以使用
wait()
让线程进入等待状态,直到条件满足时再被唤醒。
2. notify()
方法
定义
notify()
方法用于唤醒在此对象监视器上等待的单个线程。如果有多个线程在等待,则选择其中一个进行唤醒。被唤醒的线程将尝试重新获得对象的锁,并在成功获取锁后继续执行。
使用场景
- 资源竞争:在多线程环境下,当某个线程完成了对共享资源的操作并释放了锁,它可以调用
notify()
来唤醒等待该资源的线程。 - 事件驱动:在某些事件驱动的系统中,当某个事件发生时,可以调用
notify()
来通知等待该事件的线程进行处理。
3. 同步块内使用
定义
wait()
和notify()
必须在同步块或同步方法内部使用。这是因为这些方法依赖于对象的内置锁(也称为监视器锁),而只有持有锁的线程才能调用这些方法。
使用场景
- 保护临界区:在多线程编程中,临界区是一段需要互斥访问的代码。通过在同步块中使用
wait()
和notify()
,可以确保同一时间只有一个线程能够执行临界区内的代码。 - 线程协作:在需要多个线程协作完成某项任务的场景下,可以使用
wait()
和notify()
来实现线程间的协调。
4. 释放锁
定义
调用wait()
会释放锁,而调用notify()
不会释放锁。这意味着在调用wait()
之后,当前线程会释放它持有的锁,并进入等待状态;而在调用notify()
之后,当前线程仍然持有锁,直到它退出同步块或同步方法。
使用场景
- 避免死锁:通过在调用
wait()
时释放锁,可以避免死锁的发生。因为如果线程在持有锁的情况下进入等待状态,其他线程就无法获取锁,从而导致死锁。 - 提高并发性:通过在调用
notify()
时不释放锁,可以提高系统的并发性。因为被唤醒的线程可以直接继续执行,而不需要重新竞争锁。
示例代码
以下是一个使用wait()
和notify()
方法的简单示例,展示了生产者-消费者模型的基本实现:
public class ProducerConsumerExample {
private static final Object lock = new Object();
private static boolean condition = false;
private static int item = 0;
public static void main(String[] args) throws InterruptedException {
Thread producer = new Thread(() -> {
synchronized (lock) {
while (true) {
while (condition) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
item++;
System.out.println("Produced: " + item);
condition = true;
lock.notify();
}
}
});
Thread consumer = new Thread(() -> {
synchronized (lock) {
while (true) {
while (!condition) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumed: " + item);
condition = false;
lock.notify();
}
}
});
producer.start();
consumer.start();
}
}
在这个示例中,生产者线程和消费者线程通过共享的lock
对象进行同步。生产者线程在生产完一个项目后调用notify()
唤醒消费者线程;消费者线程在消费完一个项目后调用notify()
唤醒生产者线程。通过这种方式,实现了生产者和消费者之间的协调和通信。