🏭 生产者-消费者模型:奶茶工厂的奇幻之旅

55 阅读4分钟

想象你经营着一家神奇的奶茶工厂,这里有两条神奇的生产线:

  • 生产者:一群勤劳的奶茶小精灵,不断生产美味奶茶
  • 消费者:一群贪吃的独角兽,疯狂消耗这些奶茶

但问题来了:小精灵生产太快会堆积如山,独角兽吃太快会断粮!这就是我们需要解决的生产者-消费者模型问题!

🧙 魔法仓库:缓冲区(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魔法防护罩线程同步

🌈 实际应用场景

  1. Android消息机制

    • 生产者:UI线程发送消息
    • 缓冲区:MessageQueue
    • 消费者:Looper处理消息
  2. 线程池任务处理

    • 生产者:提交任务
    • 缓冲区:任务队列
    • 消费者:工作线程
  3. 网络请求处理

    • 生产者:网络请求线程
    • 缓冲区:请求队列
    • 消费者:数据处理线程

🚀 高级技巧:使用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内部已经实现了等待/通知机制,让代码更简洁!

📚 总结

生产者-消费者模型就像管理一个神奇的奶茶工厂:

  1. 仓库缓冲区平衡生产和消费速度
  2. synchronized确保线程安全访问
  3. wait/notify实现线程间高效协作
  4. 条件循环防止虚假唤醒

掌握了这个模型,你就能构建出高效、稳定的并发系统,就像经营一家永不停歇的魔法奶茶工厂!🧋✨