🌐 Object类中的wait和notify方法
📌 wait()
和 notify()
方法
- 📚 基本信息
wait()
和notify()
是 Java 中的Object
类的方法,而不是Thread
类的方法。- 这意味着每个 Java 对象都有这两个方法。
- 这些方法用于线程间的协调和通信。
📌 wait()
方法的作用
-
🔄 等待
- 当一个线程调用对象的
wait()
方法时,它会使该线程进入等待状态。 - 该线程会持续等待,直到其他线程调用同一对象的
notify()
或notifyAll()
方法。
- 当一个线程调用对象的
-
📌 示例
Object o = new Object();
synchronized(o) {
try {
o.wait(); // 当前线程进入等待状态,直到其他线程唤醒它
} catch (InterruptedException e) {
e.printStackTrace();
}
}
注意: wait()
方法必须在同步代码块或同步方法中调用。
📌 notify()
方法的作用
-
🔔 唤醒
- 当一个线程调用对象的
notify()
方法时,它会从所有在该对象上等待的线程中选择一个来唤醒。 - 如果有多个线程在同一对象上等待,只有一个会被
notify()
方法唤醒。 notifyAll()
方法则会唤醒所有在该对象上等待的线程。
- 当一个线程调用对象的
-
📌 示例
Object o = new Object();
synchronized(o) {
o.notify(); // 唤醒在该对象上等待的一个线程
}
注意: notify()
和 notifyAll()
也必须在同步代码块或同步方法中调用。
🌐 生产者和消费者模式
生产者和消费者模式是多线程开发中常见的设计模式,用于解决线程间的协调问题。其中,生产者负责生成数据,消费者负责处理数据。这两个角色通过一个共享的数据结构(如队列)进行通信。
📌 基本思想
-
🏭 生产者
- 负责产生数据。
- 当数据结构满时,需要暂停并等待消费者处理数据。
-
🛍️ 消费者
- 负责处理数据。
- 当数据结构为空时,需要暂停并等待生产者产生数据。
📌 关键点
- 条件检查
- 使用
while
循环而不是if
语句来检查条件。 - 保证当线程被唤醒后,条件仍然满足。
- 锁的释放和获取
- 调用
wait()
方法会释放锁。 - 其他线程可以获取锁并执行。
notify()
或notifyAll()
则是用于唤醒等待的线程。
- 公共的锁对象
wait()
和notify()
方法需要在同一个锁对象上调用。
🎯 核心问题
- 使用
synchronized
可以解决多线程的竞争问题。 - 但
synchronized
并没有解决多线程间的协调问题。
⚙️ 场景描述
- 如果生产者生产的数据过快,消费者消费的速度跟不上。
- 存放数据的地方(如内存缓冲区)很快就会满。
- 如果消费者消费能力大于生产者,缓冲区很快就空了。
⚠️ 注意: 这时,消费者必须等待生产者生产数据;反之,生产者必须等待消费者消费数据。
🔗 wait
和 notify
的协调作用
- 在
synchronized
内部可以调用wait()
使线程进入等待状态。 - 必须在已获得的锁对象上调用
wait()
方法。 - 在
synchronized
内部可以调用notify()
或notifyAll()
唤醒其他等待线程。 - 必须在已获得的锁对象上调用
notify()
或notifyAll()
方法。 - 已唤醒的线程还需要重新获得锁后才能继续执行。
📌 Java中的实现
Java中,可以使用Object
类的wait()
和notify()
或notifyAll()
方法来实现生产者和消费者模式。这些方法可以帮助我们在条件不满足时使线程进入等待状态,并在条件满足时唤醒线程。
📌 示例解析
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerExample {
public static void main(String[] args) {
TaskQueue taskQueue = new TaskQueue();
Thread producer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
taskQueue.addTask("Task " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Producer");
Thread consumer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
String task = taskQueue.getTask();
System.out.println("Executing: " + task);
}
}, "Consumer");
producer.start();
consumer.start();
}
}
class TaskQueue {
private Queue<String> queue = new LinkedList<>();
public synchronized void addTask(String task) {
queue.add(task);
notify();
}
public synchronized String getTask() {
while (queue.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return queue.poll();
}
}
📌 说明
TaskQueue
类是共享数据结构,生产者往其中添加任务,消费者从中取出任务。- 生产者线程和消费者线程分别是两个独立的线程。生产者每0.5秒生产一个任务,消费者则尝试获取并执行任务。
- 当队列为空时,消费者会进入等待状态;生产者在添加任务后会唤醒等待的消费者线程。
这只是一个简单的生产者和消费者模式的实现。在实际应用中,可能还需要考虑更多细节和其他因素,例如支持多个生产者和消费者、设置队列的最大容量、处理异常等。
📌 总结
生产者和消费者模式是解决线程间协调问题的经典模式。在Java中,通过Object
类的wait()
和notify()
方法,我们可以很容易地实现这个模式。但要确保正确地使用这些方法,避免出现死锁或线程饿死的情况。