想象你经营着一家神奇的奶茶工厂,这里有两条神奇的生产线:
- 生产者:一群勤劳的奶茶小精灵,不断生产美味奶茶
- 消费者:一群贪吃的独角兽,疯狂消耗这些奶茶
但问题来了:小精灵生产太快会堆积如山,独角兽吃太快会断粮!这就是我们需要解决的生产者-消费者模型问题!
🧙 魔法仓库:缓冲区(Buffer)
java
import java.util.LinkedList;
import java.util.Queue;
class MagicWarehouse {
private final Queue<String> milkTeas = new LinkedList<>(); // 奶茶存储区
private final int capacity; // 仓库最大容量
private int productionCount = 0; // 生产计数
private int consumptionCount = 0; // 消费计数
public MagicWarehouse(int capacity) {
this.capacity = capacity;
}
// 生产奶茶(同步方法)
public synchronized void produce() throws InterruptedException {
// 仓库满了就等待
while (milkTeas.size() == capacity) {
System.out.println("⚠️ 仓库满了!" + Thread.currentThread().getName() + "等待中...");
wait();
}
String tea = "🍵奶茶#" + (++productionCount);
milkTeas.add(tea);
System.out.println(Thread.currentThread().getName() + " 生产了 " + tea +
" | 库存: " + milkTeas.size());
// 通知等待的消费者
notifyAll();
}
// 消费奶茶(同步方法)
public synchronized void consume() throws InterruptedException {
// 仓库空了就等待
while (milkTeas.isEmpty()) {
System.out.println("⚠️ 仓库空了!" + Thread.currentThread().getName() + "等待中...");
wait();
}
String tea = milkTeas.poll();
System.out.println(Thread.currentThread().getName() + " 消费了 " + tea +
" | 库存: " + milkTeas.size() + " | 共消费: " + (++consumptionCount));
// 通知等待的生产者
notifyAll();
}
}
👷 生产者:奶茶小精灵
java
class Producer extends Thread {
private final MagicWarehouse warehouse;
public Producer(String name, MagicWarehouse warehouse) {
super(name);
this.warehouse = warehouse;
}
@Override
public void run() {
try {
while (true) {
warehouse.produce(); // 生产奶茶
Thread.sleep((long)(Math.random() * 800)); // 随机生产速度
}
} catch (InterruptedException e) {
System.out.println(getName() + " 被中断了");
}
}
}
🦄 消费者:奶茶独角兽
java
class Consumer extends Thread {
private final MagicWarehouse warehouse;
public Consumer(String name, MagicWarehouse warehouse) {
super(name);
this.warehouse = warehouse;
}
@Override
public void run() {
try {
while (true) {
warehouse.consume(); // 消费奶茶
Thread.sleep((long)(Math.random() * 1500)); // 随机消费速度
}
} catch (InterruptedException e) {
System.out.println(getName() + " 被中断了");
}
}
}
🌟 启动魔法工厂
java
public class MagicalTeaFactory {
public static void main(String[] args) {
MagicWarehouse warehouse = new MagicWarehouse(5); // 容量为5的仓库
// 创建2个生产者(奶茶小精灵)
Producer p1 = new Producer("🧚小精灵A", warehouse);
Producer p2 = new Producer("🧚小精灵B", warehouse);
// 创建3个消费者(奶茶独角兽)
Consumer c1 = new Consumer("🦄独角兽1号", warehouse);
Consumer c2 = new Consumer("🦄独角兽2号", warehouse);
Consumer c3 = new Consumer("🦄独角兽3号", warehouse);
// 启动所有线程
p1.start();
p2.start();
c1.start();
c2.start();
c3.start();
// 让工厂运行10秒
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 优雅地停止所有线程
p1.interrupt();
p2.interrupt();
c1.interrupt();
c2.interrupt();
c3.interrupt();
}
}
🎭 运行结果(示例片段)
text
🧚小精灵A 生产了 🍵奶茶#1 | 库存: 1
🧚小精灵B 生产了 🍵奶茶#2 | 库存: 2
🦄独角兽1号 消费了 🍵奶茶#1 | 库存: 1 | 共消费: 1
🧚小精灵A 生产了 🍵奶茶#3 | 库存: 2
🧚小精灵B 生产了 🍵奶茶#4 | 库存: 3
🦄独角兽2号 消费了 🍵奶茶#2 | 库存: 2 | 共消费: 2
🧚小精灵A 生产了 🍵奶茶#5 | 库存: 3
🧚小精灵B 生产了 🍵奶茶#6 | 库存: 4
⚠️ 仓库满了!🧚小精灵A等待中...
🦄独角兽3号 消费了 🍵奶茶#3 | 库存: 3 | 共消费: 3
🧚小精灵A 生产了 🍵奶茶#7 | 库存: 4
⚠️ 仓库空了!🦄独角兽1号等待中...
🧚小精灵B 生产了 🍵奶茶#8 | 库存: 5
🦄独角兽2号 消费了 🍵奶茶#4 | 库存: 4 | 共消费: 4
...
🧠 核心机制解析(结合故事)
1. 同步锁(synchronized)
- 魔法防护罩:一次只允许一个小精灵或独角兽进入仓库
- 保证生产和消费操作不会同时进行,避免数据混乱
2. 等待(wait)和通知(notifyAll)
- 仓库满了:小精灵们睡觉等待(
wait),直到独角兽消费后唤醒(notifyAll) - 仓库空了:独角兽们睡觉等待(
wait),直到小精灵生产后唤醒(notifyAll)
3. 条件循环(while)
java
while (milkTeas.size() == capacity) {
wait();
}
- 双重检查:被唤醒后再次检查仓库状态,防止"虚假唤醒"
- 就像小精灵被唤醒后,仍要确认仓库是否有空间
4. 缓冲区(仓库)
- 解耦生产消费:小精灵和独角兽不需要知道对方存在
- 平滑流量波动:生产快时积累库存,消费快时消耗库存
💡 生产者-消费者模型精髓
| 概念 | 现实比喻 | 编程意义 |
|---|---|---|
| 生产者 | 奶茶小精灵 | 数据生成线程 |
| 消费者 | 奶茶独角兽 | 数据处理线程 |
| 缓冲区 | 魔法仓库 | 共享队列 |
| wait() | 睡觉等待 | 线程挂起 |
| notifyAll() | 唤醒通知 | 线程唤醒 |
| synchronized | 魔法防护罩 | 线程同步 |
🌈 实际应用场景
-
Android消息机制:
- 生产者:UI线程发送消息
- 缓冲区:MessageQueue
- 消费者:Looper处理消息
-
线程池任务处理:
- 生产者:提交任务
- 缓冲区:任务队列
- 消费者:工作线程
-
网络请求处理:
- 生产者:网络请求线程
- 缓冲区:请求队列
- 消费者:数据处理线程
🚀 高级技巧:使用BlockingQueue
Java提供了更简单的实现方式:
java
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class SimpleWarehouse {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>(5);
public void produce(String item) throws InterruptedException {
queue.put(item); // 自动阻塞
System.out.println("生产: " + item);
}
public String consume() throws InterruptedException {
String item = queue.take(); // 自动阻塞
System.out.println("消费: " + item);
return item;
}
}
BlockingQueue内部已经实现了等待/通知机制,让代码更简洁!
📚 总结
生产者-消费者模型就像管理一个神奇的奶茶工厂:
- 仓库缓冲区平衡生产和消费速度
- synchronized确保线程安全访问
- wait/notify实现线程间高效协作
- 条件循环防止虚假唤醒
掌握了这个模型,你就能构建出高效、稳定的并发系统,就像经营一家永不停歇的魔法奶茶工厂!🧋✨