生产者-消费者模式是并发编程中的经典问题,用于解决线程间协作和数据交换的需求。以下是 Java 中实现该模式的 5 种主流方式,从基础到高级逐步深入:
1. 使用 wait() 和 notify()(最基础方式)
class Buffer {
private Queue<Integer> queue = new LinkedList<>();
private int capacity;
public Buffer(int capacity) {
this.capacity = capacity;
}
public synchronized void produce(int item) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // 缓冲区满,生产者等待
}
queue.add(item);
System.out.println("Produced: " + item);
notifyAll(); // 唤醒消费者
}
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 缓冲区空,消费者等待
}
int item = queue.poll();
System.out.println("Consumed: " + item);
notifyAll(); // 唤醒生产者
return item;
}
}
特点:
- 基于
Object的监视器机制(synchronized+wait()/notify())。 - 需要手动处理线程阻塞和唤醒。
2. 使用 BlockingQueue(推荐方式)
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); // 容量为10
// 生产者
Runnable producer = () -> {
try {
int item = (int) (Math.random() * 100);
queue.put(item); // 队列满时自动阻塞
System.out.println("Produced: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 消费者
Runnable consumer = () -> {
try {
int item = queue.take(); // 队列空时自动阻塞
System.out.println("Consumed: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 启动线程
new Thread(producer).start();
new Thread(consumer).start();
特点:
- 直接利用
BlockingQueue的线程安全特性(底层基于ReentrantLock+Condition)。 - 无需手动同步,代码简洁。
3. 使用 Lock 和 Condition(更灵活)
class BufferWithLock {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private Queue<Integer> queue = new LinkedList<>();
private int capacity;
public BufferWithLock(int capacity) {
this.capacity = capacity;
}
public void produce(int item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 等待"非满"条件
}
queue.add(item);
notEmpty.signal(); // 唤醒一个消费者
} finally {
lock.unlock();
}
}
public int consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 等待"非空"条件
}
int item = queue.poll();
notFull.signal(); // 唤醒一个生产者
return item;
} finally {
lock.unlock();
}
}
}
特点:
- 更细粒度的控制(可区分“非空”和“非满”条件)。
- 支持公平锁、可中断等高级特性。
4. 使用 Semaphore(信号量控制)
class BufferWithSemaphore {
private Queue<Integer> queue = new LinkedList<>();
private Semaphore emptySlots;
private Semaphore filledSlots;
private Semaphore mutex;
public BufferWithSemaphore(int capacity) {
emptySlots = new Semaphore(capacity); // 初始空闲容量
filledSlots = new Semaphore(0); // 初始已填充容量
mutex = new Semaphore(1); // 互斥锁
}
public void produce(int item) throws InterruptedException {
emptySlots.acquire(); // 获取空闲槽位
mutex.acquire(); // 互斥访问队列
queue.add(item);
mutex.release();
filledSlots.release(); // 增加已填充槽位
}
public int consume() throws InterruptedException {
filledSlots.acquire(); // 获取已填充槽位
mutex.acquire();
int item = queue.poll();
mutex.release();
emptySlots.release(); // 释放空闲槽位
return item;
}
}
特点:
- 通过信号量直接控制资源数量。
- 适合复杂资源分配场景。
5. 使用 Exchanger(单生产者单消费者交换)
Exchanger<String> exchanger = new Exchanger<>();
// 生产者
Runnable producer = () -> {
try {
String data = "Produced Data";
System.out.println("Producer sends: " + data);
data = exchanger.exchange(data); // 阻塞直到消费者交换数据
System.out.println("Producer received: " + data);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 消费者
Runnable consumer = () -> {
try {
String data = exchanger.exchange(null); // 阻塞直到生产者发送数据
System.out.println("Consumer received: " + data);
data = "Consumed Data";
exchanger.exchange(data); // 返回处理后的数据
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
new Thread(producer).start();
new Thread(consumer).start();
特点:
- 适用于严格交替执行的生产者-消费者模型。
- 每次只能交换一个数据项。
对比总结
| 实现方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
wait()/notify() | 传统同步场景 | 无需额外依赖 | 需手动处理条件判断 |
BlockingQueue | 大多数标准场景(推荐) | 代码简洁,线程安全 | 灵活性较低 |
Lock + Condition | 需要精细控制的场景 | 支持多个条件队列 | 代码稍复杂 |
Semaphore | 资源数量固定的场景(如连接池) | 直观控制资源 | 需额外互斥锁 |
Exchanger | 单对单严格交替场景 | 无需缓冲区 | 仅限一对一通信 |
最佳实践建议
- 优先选择
BlockingQueue:适用于 90% 的标准场景。 - 需要超时控制时用
Lock + Condition:例如await(timeout, unit)。 - 避免
wait()/notify()的误用:容易导致死锁或虚假唤醒。
通过合理选择实现方式,可以高效解决生产者-消费者问题!

1404

被折叠的 条评论
为什么被折叠?



