1. 生产者-消费者模式:从厨房看并发编程
想象一下繁忙的餐厅后厨:厨师(生产者)不断制作菜品,服务员(消费者)将做好的菜品端给顾客。当厨师做菜太快而服务员来不及上菜时,菜品会在传菜区堆积;反之如果服务员太勤快而厨师跟不上节奏,传菜区就会空空如也——这就是生产者-消费者模式的现实映射。
在编程世界中,这种模式用于解决不同执行速度的线程/进程间的协同问题。典型场景包括:
- 日志系统中:应用线程(生产者)快速生成日志 vs 磁盘写入(消费者)较慢
- 消息队列中:消息发布者(生产者)突发流量 vs 消费者处理能力有限
- 视频流处理:视频采集(生产者)固定帧率 vs AI分析(消费者)波动性能
关键点:生产者和消费者通过共享的缓冲区(BlockingQueue)解耦,任何一方速度波动都不会直接影响另一方,就像餐厅的传菜区缓解了厨房和前厅的压力差。
2. Java实现:一个BlockingQueue搞定线程安全
用Java实现时,java.util.concurrent.BlockingQueue 是最佳选择。以下是完整代码示例:
java复制import java.util.concurrent.*;
public class Kitchen {
private static BlockingQueue<String> dishQueue = new LinkedBlockingQueue<>(5);
// 厨师线程
static class Chef implements Runnable {
@Override
public void run() {
try {
for (int i=1; ; i++) {
String dish = "菜品-" + i;
dishQueue.put(dish); // 队列满时自动阻塞
System.out.println("生产: " + dish);
Thread.sleep(300); // 模拟做菜时间
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 服务员线程
static class Waiter implements Runnable {
@Override
public void run() {
try {
while (true) {
String dish = dishQueue.take(); // 队列空时自动阻塞
System.out.println("消费: " + dish);
Thread.sleep(500); // 模拟上菜时间
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
new Thread(new Chef()).start();
new Thread(new Waiter()).start();
}
}
这段代码的精妙之处在于:
LinkedBlockingQueue的容量限制(5个菜品)自动实现了流量控制put()和take()方法内置了阻塞机制,无需手动实现wait/notify- 中断处理保障了线程能安全退出
运行时会看到类似输出:
code复制生产: 菜品-1
消费: 菜品-1
生产: 菜品-2
生产: 菜品-3
消费: 菜品-2
生产: 菜品-4
...
3. 深度解析:BlockingQueue的四大武器
为什么这个实现既简单又可靠?关键在于BlockingQueue提供的四个核心机制:
