本文目录导读:

我来为你提供一个完整的 wait/notify 生产者-消费者案例,并详细解释其工作原理。
经典生产者-消费者案例
import java.util.LinkedList;
import java.util.Queue;
/**
* 使用 wait/notify 实现生产者-消费者模式
*/
public class ProducerConsumerExample {
// 共享缓冲区
private final Queue<Integer> buffer = new LinkedList<>();
private final int MAX_SIZE = 5; // 缓冲区最大容量
/**
* 生产者:往缓冲区添加数据
*/
public synchronized void produce() throws InterruptedException {
// 当缓冲区已满时,生产者等待
while (buffer.size() == MAX_SIZE) {
System.out.println("【生产者】缓冲区已满,等待消费者消费...");
wait(); // 释放锁并等待
}
// 生产数据
int data = (int) (Math.random() * 100);
buffer.offer(data);
System.out.println("【生产者】生产数据: " + data + ",当前缓冲区大小: " + buffer.size());
// 通知消费者可以消费了
notifyAll();
// 模拟生产过程耗时
Thread.sleep(500);
}
/**
* 消费者:从缓冲区取出数据
*/
public synchronized void consume() throws InterruptedException {
// 当缓冲区为空时,消费者等待
while (buffer.isEmpty()) {
System.out.println("【消费者】缓冲区为空,等待生产者生产...");
wait(); // 释放锁并等待
}
// 消费数据
int data = buffer.poll();
System.out.println("【消费者】消费数据: " + data + ",当前缓冲区大小: " + buffer.size());
// 通知生产者可以继续生产了
notifyAll();
// 模拟消费过程耗时
Thread.sleep(500);
}
/**
* 测试主方法
*/
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
// 创建生产者线程
Thread producerThread = new Thread(() -> {
try {
while (true) {
example.produce();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "生产者线程");
// 创建消费者线程
Thread consumerThread = new Thread(() -> {
try {
while (true) {
example.consume();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "消费者线程");
// 启动线程
producerThread.start();
consumerThread.start();
}
}
改进版本(支持多个生产者消费者)
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 多生产者-多消费者案例
*/
public class MultiProducerConsumer {
private final Queue<Integer> buffer = new LinkedList<>();
private final int MAX_SIZE = 3;
private final AtomicInteger counter = new AtomicInteger(0);
// 锁对象
private final Object lock = new Object();
/**
* 生产者方法
*/
public void produce(String producerName) throws InterruptedException {
synchronized (lock) {
// 使用 while 循环检查条件(防止虚假唤醒)
while (buffer.size() >= MAX_SIZE) {
System.out.println(producerName + ": 缓冲区已满,等待...");
lock.wait(); // 释放 lock 锁
}
int data = counter.incrementAndGet();
buffer.offer(data);
System.out.println(producerName + ": 生产了 " + data + ",当前大小: " + buffer.size());
// 通知所有等待的线程
lock.notifyAll();
Thread.sleep(300);
}
}
/**
* 消费者方法
*/
public void consume(String consumerName) throws InterruptedException {
synchronized (lock) {
// 使用 while 循环检查条件(防止虚假唤醒)
while (buffer.isEmpty()) {
System.out.println(consumerName + ": 缓冲区为空,等待...");
lock.wait(); // 释放 lock 锁
}
int data = buffer.poll();
System.out.println(consumerName + ": 消费了 " + data + ",当前大小: " + buffer.size());
// 通知所有等待的线程
lock.notifyAll();
Thread.sleep(300);
}
}
public static void main(String[] args) {
MultiProducerConsumer example = new MultiProducerConsumer();
// 创建多个生产者
for (int i = 1; i <= 2; i++) {
final String producerName = "生产者" + i;
new Thread(() -> {
try {
while (true) {
example.produce(producerName);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
// 创建多个消费者
for (int i = 1; i <= 2; i++) {
final String consumerName = "消费者" + i;
new Thread(() -> {
try {
while (true) {
example.consume(consumerName);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}
更实用的阻塞队列实现案例
import java.util.LinkedList;
import java.util.Queue;
/**
* 自定义阻塞队列(类似 BlockingQueue 的简单实现)
*/
public class CustomBlockingQueue<T> {
private final Queue<T> queue;
private final int capacity;
private final Object lock = new Object();
public CustomBlockingQueue(int capacity) {
this.queue = new LinkedList<>();
this.capacity = capacity;
}
/**
* 添加元素(如果队列满了,则阻塞)
*/
public void put(T item) throws InterruptedException {
synchronized (lock) {
while (queue.size() >= capacity) {
System.out.println("队列已满," + Thread.currentThread().getName() + " 等待...");
lock.wait();
}
queue.offer(item);
System.out.println(Thread.currentThread().getName() + " 添加: " + item
+ ",队列大小: " + queue.size());
lock.notifyAll();
}
}
/**
* 取出元素(如果队列空了,则阻塞)
*/
public T take() throws InterruptedException {
synchronized (lock) {
while (queue.isEmpty()) {
System.out.println("队列为空," + Thread.currentThread().getName() + " 等待...");
lock.wait();
}
T item = queue.poll();
System.out.println(Thread.currentThread().getName() + " 取出: " + item
+ ",队列大小: " + queue.size());
lock.notifyAll();
return item;
}
}
/**
* 测试
*/
public static void main(String[] args) {
CustomBlockingQueue<Integer> queue = new CustomBlockingQueue<>(3);
// 生产者
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
queue.put(i);
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "生产者").start();
// 消费者
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
queue.take();
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "消费者").start();
}
}
关键要点和注意事项
wait/notify 必须配合 synchronized 使用
// 正确写法
synchronized (lock) {
while (condition) {
lock.wait();
}
lock.notifyAll();
}
使用 while 而不是 if 检查条件
// ❌ 错误:使用 if 可能产生虚假唤醒问题
if (condition) {
wait();
}
// ✅ 正确:使用 while 防止虚假唤醒
while (condition) {
wait();
}
wait 释放锁的机制
wait()释放对象锁并让出 CPUnotify()随机唤醒一个等待线程(不释放锁)notifyAll()唤醒所有等待线程(不释放锁)
常用方法说明
| 方法 | 作用 | 锁的处理 |
|---|---|---|
wait() |
让当前线程等待 | 释放锁 |
wait(long timeout) |
等待指定时间 | 释放锁 |
notify() |
唤醒一个等待线程 | 不释放锁 |
notifyAll() |
唤醒所有等待线程 | 不释放锁 |
两个线程死锁的简单示例
public class DeadlockExample {
public static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
// 线程1
new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread1: 持有 lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("Thread1: 持有 lock1 和 lock2");
}
}
}).start();
// 线程2(与线程1顺序相反,容易造成死锁)
new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread2: 持有 lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("Thread2: 持有 lock1 和 lock2");
}
}
}).start();
}
}
生产环境建议
在实际生产环境中,推荐使用 java.util.concurrent 包提供的工具类:
// 使用阻塞队列
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
// 生产者
new Thread(() -> {
try {
queue.put(data); // 自动阻塞
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者
new Thread(() -> {
try {
int data = queue.take(); // 自动阻塞
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
这些标准库组件已经处理好了线程安全、性能优化等问题,可以减少手动编写 wait/notify 代码。