使用wait()
和notify()
方法解决生产者-消费者问题是一个经典的多线程同步问题。在这个问题中,我们有一个生产者线程和一个或多个消费者线程共享一个有限容量的缓冲区。生产者生成产品放入缓冲区,消费者从缓冲区中取出产品。
以下是一个简单的Java实现示例,使用了wait()
和notify()
来同步生产者和消费者线程。注意,为了避免虚假唤醒(spurious wakeup),我们通常会在循环中检查条件。
public class ProducerConsumerExample {
private final int capacity;
private int count = 0;
private final Object lock = new Object();
private int[] buffer;
public ProducerConsumerExample(int capacity) {
this.capacity = capacity;
this.buffer = new int[capacity];
}
// 生产者线程
public void produce(int item) throws InterruptedException {
synchronized (lock) {
// 等待缓冲区不满
while (count == capacity) {
lock.wait();
}
// 插入元素
buffer[count] = item;
System.out.println("Produced: " + item);
count++;
// 通知消费者
lock.notify();
}
}
// 消费者线程
public int consume() throws InterruptedException {
synchronized (lock) {
// 等待缓冲区不为空
while (count == 0) {
lock.wait();
}
// 取出元素
int item = buffer[count - 1];
count--;
System.out.println("Consumed: " + item);
// 通知生产者
lock.notify();
return item;
}
}
public static void main(String[] args) {
ProducerConsumerExample pc = new ProducerConsumerExample(5);
// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
pc.produce(i);
Thread.sleep(100); // 模拟耗时操作
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
pc.consume();
Thread.sleep(150); // 模拟耗时操作
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
注意:
lock
对象用于同步produce()
和consume()
方法,确保线程安全。- 使用
while
循环来检查条件(例如缓冲区是否满或空),以防止虚假唤醒导致的问题。 wait()
和notify()
方法必须在同步块中调用,且必须锁定同一个对象。- 在生产者和消费者方法中,调用
wait()
后线程将被阻塞,直到其他线程调用同一对象的notify()
或notifyAll()
方法。 notifyAll()
通常比notify()
更安全,因为它可以唤醒所有等待的线程,但在本例中,由于每次只有一个线程能执行(生产者或消费者),使用notify()
已足够。然而,在多生产者或多消费者的情况下,建议使用notifyAll()
。