【从零开始学Java | 第四十二篇】生产者消费者问题(等待唤醒机制)

目录

前言

一、什么是"生产者-消费者"模型?

1.经典实现(wait()、notifyAll())

2.阻塞队列实现(BlockingQueue)

①阻塞队列的继承结构

总结


前言

之前学习多线程所执行的代码我们可以看到他们的执行都是随机的,哪个电影院窗口卖出一张门票是不固定的,但是如果我们想要让线程执行的顺序固定,应该如何操作呢?

一、什么是"生产者-消费者"模型?

为了方便理解,我们打个最通俗的比方:包子铺。

  • 生产者**(Producer)**:后厨做包子的师傅。

  • 消费者**(Consumer)**:在前台买包子的顾客。

  • 缓冲区**(Buffer)**:装包子的蒸笼(容量有限,比如最多装 10 个)。

如果没有蒸笼,师傅做好一个包子,必须亲手交到顾客手里才能回去做下一个。如果顾客还没来,师傅就只能傻等着;反过来,如果师傅做得慢,顾客也得干等着。

1.经典实现(wait()、notifyAll())

桌子:定义了锁、消费者可以消费的总数、判断桌子上是否有包子。

java 复制代码
public class Desk {
    //锁
    public static Object obj = new Object();

    //总数
    public static int count = 10;

    //flag判空
    public static int flag = 0;

}

消费者线程:

java 复制代码
//消费者线程
public class Consumer extends Thread{
    @Override
    public void run() {
        while(true){
            synchronized (Desk.obj){
                if(Desk.count == 0){    //1.消费者吃饱了
                    break;
                } else {    //2.消费者没吃饱
                    if(Desk.flag == 0){     //2.1桌子上没食物
                        try {
                            Desk.obj.wait();        //2.1.1等待
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {        //2.2桌子上有食物
                        Desk.count--;   //2.2.1开吃
                        Desk.flag = 0;
                        System.out.println(getName() + "正在吃东西,还能再吃" + Desk.count + "份食物");
                        Desk.obj.notifyAll();   //2.2.2叫醒生产者.
                    }
                }
            }
        }
    }
}

生产者线程:

java 复制代码
//生产者线程
public class Productor extends Thread{
    @Override
    public void run() {
        while(true){
            synchronized (Desk.obj){
                if(Desk.count == 0){    //1.消费者吃饱了,就不用做了
                    break;
                } else {                //2.消费者没吃饱
                    if(Desk.flag == 1){     //2.1桌子上是有食物的
                        try {
                            Desk.obj.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else{                 //2.2桌子上没食物
                        Desk.flag = 1;              //2.2.1做食物
                        System.out.println(getName() + "正在做食物");
                        Desk.obj.notifyAll();
                    }
                }
            }
        }
    }
}

测试:

java 复制代码
public class Test {
    public static void main(String[] args) {
        Consumer c = new Consumer();
        Productor p = new Productor();

        c.setName("消费者");
        p.setName("生产者");

        c.start();
        p.start();
    }
}

运行结果:

2.阻塞队列实现(BlockingQueue)

①阻塞队列的继承结构

接口:Itreable、Collection、Queue、BlockingQueue

实现类:

  • ArrayBlockingQueue:底层是数组,有界。
  • LinkedBlockingQueue:底层是链表,无界,但不是真正的无界,最大为int的最大值。

消费者线程:

java 复制代码
public class Consumer extends Thread{
    ArrayBlockingQueue<String> queue;

    public Consumer(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while(true){
            try {
                String take = queue.take();

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

生产者线程:

java 复制代码
public class Productor extends Thread{
    ArrayBlockingQueue<String> queue;

    public Productor(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            queue.put("包子");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

测试:

java 复制代码
public class Test {
    public static void main(String[] args) {
        //定义阻塞队列
        ArrayBlockingQueue<String> abq = new ArrayBlockingQueue<>(1);

        Consumer c = new Consumer(abq);
        Productor p = new Productor(abq);

        c.start();
        p.start();
    }
}

总结

生产者-消费者模型并不是一个具体的类,而是一种极具智慧的架构思想。

  1. 解耦:生产者和消费者彼此不直接打交道,只和缓冲区交互。

  2. 异步:大家各干各的,互不干扰。

  3. 缓冲:完美解决生产和消费速度不匹配的问题。

😁😁

相关推荐
@大迁世界几秒前
告别 Go,拥抱 Rust
开发语言·golang·rust
SEO_juper2 分钟前
Node.js 重定向链检测工具:301 跳了几跳、有没有成环,一次看清
xml·开发语言·python·node.js·json·bash
南归北隐3 分钟前
Spring AI Alibaba Graph框架实现MCP数据库查询实战
java·后端·spring ai·mcp·spring ai mcp
学逆向的4 分钟前
C 指针常量与常量指针
c语言·开发语言·网络安全
古法老登瞎折腾16 分钟前
大数据智能运维实战(九):让 AI 查日志——找到异常,也保留上下文
java·大数据·运维·人工智能·spring ai·mcp
ai大模型-探索者27 分钟前
Function Call与MCP(区别、联系与实战)
java·服务器·网络
略略略咯咯32 分钟前
啊啊啊啊啊
java·服务器·数据库
SL_staff1 小时前
运维救火实战:钉钉API变更致薪资计算中断,如何用可视化逻辑引擎5分钟热修复
java·开源·全栈
低调滴开发1 小时前
3 行代码写崩一个函数?切片才是 Rust 字符串处理的正确姿势
开发语言·后端·rust
CodeYp20191 小时前
第15讲:二叉搜索树
开发语言·c++·二叉搜索树