13. Java 生产者与消费者案例

news2024/10/7 9:25:31

1. 前言

本节内容是通过之前学习的 synchronized 关键字,实现多线程并发编程中最经典的生产者与消费者模式,这是本节课程的核心内容,所有的知识点都是围绕这一经典模型展开的。本节有如下知识点:

  • 生产者与消费者模型介绍,这是打开本节知识大门的钥匙,也是本节内容的基础;
  • 了解生产者与消费者案例实现的三种方式,我们本节以 synchronized 关键字联合 wait/notify 机制进行实现;
  • wait 方法和 notify 方法介绍,这是我们实现生产者与消费者案例的技术基础;
  • 生产者与消费者案例代码实现,这是我们本节内容的核心,一定要对此知识点进行深入的学习和掌握。

2. 生产者与消费者模型介绍

定义: 生产者消费者模式是一个十分经典的多线程并发协作的模式。

意义:弄懂生产者消费者问题能够让我们对并发编程的理解加深。

介绍:所谓生产者 - 消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域。

共享的数据区域就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为。而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。

3. 生产者与消费者三种实现方式

在实现生产者消费者问题时,可以采用三种方式:

  • 使用 Object 的 wait/notify 的消息通知机制,本节课程我们采用该方式结合 synchronized 关键字进行生产者与消费者模式的实现;
  • 使用 Lock 的 Condition 的 await/signal 的消息通知机制;
  • 使用 BlockingQueue 实现。本文主要将这三种实现方式进行总结归纳。

4. wait 与 notify

Java 中,可以通过配合调用 Object 对象的 wait () 方法和 notify () 方法或 notifyAll () 方法来实现线程间的通信。

wait 方法:我们之前对 wait 方法有了基础的了解,在线程中调用 wait () 方法,将阻塞当前线程,并且释放锁,直至等到其他线程调用了调用 notify () 方法或 notifyAll () 方法进行通知之后,当前线程才能从 wait () 方法出返回,继续执行下面的操作。

notify 方法:即唤醒,notify 方法使原来在该对象上 wait 的线程退出 waiting 状态,使得该线程从等待队列中移入到同步队列中去,等待下一次能够有机会获取到对象监视器锁。

notifyAll 方法:即唤醒全部 waiting 线程,与 notify 方法在效果上一致。

5. 生产者与消费者案例

为了更好地理解并掌握生产者与消费者模式的实现,我们先来进行场景设计,然后再通过实例代码进行实现并观察运行结果。

场景设计

  • 创建一个工厂类 ProductFactory,该类包含两个方法,produce 生产方法和 consume 消费方法;
  • 对于 produce 方法,当没有库存或者库存达到 10 时,停止生产。为了更便于观察结果,每生产一个产品,sleep 5000 毫秒;
  • 对于 consume 方法,只要有库存就进行消费。为了更便于观察结果,每消费一个产品,sleep 5000 毫秒;
  • 库存使用 LinkedList 进行实现,此时 LinkedList 即共享数据内存;
  • 创建一个 Producer 生产者类,用于调用 ProductFactory 的 produce 方法。生产过程中,要对每个产品从 0 开始进行编号;
  • 创建一个 Consumer 消费者类,用于调用 ProductFactory 的 consume 方法;
  • 创建一个测试类,main 函数中创建 2 个生产者和 3 个消费者,运行程序进行结果观察。

实例:创建一个工厂类 ProductFactory

class ProductFactory {
    private LinkedList<String> products; //根据需求定义库存,用 LinkedList 实现
    private int capacity = 10; // 根据需求:定义最大库存 10
    public ProductFactory() {
        products = new LinkedList<String>();
    }
    // 根据需求:produce 方法创建
    public synchronized void produce(String product) {
        while (capacity == products.size()) { //根据需求:如果达到 10 库存,停止生产
            try {
                System.out.println("警告:线程("+Thread.currentThread().getName() + ")准备生产产品,但产品池已满");
                wait(); // 库存达到 10 ,生产线程进入 wait 状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        products.add(product); //如果没有到 10 库存,进行产品添加
        try {
            Thread.sleep(5000); //根据需求为了便于观察结果,每生产一个产品,sleep 5000 ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程("+Thread.currentThread().getName() + ")生产了一件产品:" + product+";当前剩余商品"+products.size()+"个");
        notify(); //生产了产品,通知消费者线程从 wait 状态唤醒,进行消费
    }

    // 根据需求:consume 方法创建
    public synchronized String consume() {
        while (products.size()==0) { //根据需求:没有库存消费者进入wait状态
            try {
                System.out.println("警告:线程("+Thread.currentThread().getName() + ")准备消费产品,但当前没有产品");
                wait(); //库存为 0 ,无法消费,进入 wait ,等待生产者线程唤醒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        String product = products.remove(0) ; //如果有库存则消费,并移除消费掉的产品
        try {
            Thread.sleep(5000);//根据需求为了便于观察结果,每消费一个产品,sleep 5000 ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程("+Thread.currentThread().getName() + ")消费了一件产品:" + product+";当前剩余商品"+products.size()+"个");
        notify();// 通知生产者继续生产
        return product;
    }
}

实例:Producer 生产者类创建

class Producer implements Runnable {
    private ProductFactory productFactory; //关联工厂类,调用 produce 方法
    public Producer(ProductFactory productFactory) {
        this.productFactory = productFactory;
    }
    public void run() {
        int i = 0 ; // 根据需求,对产品进行编号
        while (true) {
            productFactory.produce(String.valueOf(i)); //根据需求 ,调用 productFactory 的 produce 方法
            i++;
        }
    }
}

实例:Consumer 消费者类创建

class Consumer implements Runnable {
    private ProductFactory productFactory;
    public Consumer(ProductFactory productFactory) {
        this.productFactory = productFactory;
    }
    public void run() {
        while (true) {
            productFactory.consume();
        }
    }
}

实例: 创建测试类,2 个生产者,3 个消费者

public class DemoTest extends Thread{
    public static void main(String[] args) {
        ProductFactory productFactory = new ProductFactory();
        new Thread(new Producer(productFactory),"1号生产者"). start();
        new Thread(new Producer(productFactory),"2号生产者"). start();
        new Thread(new Consumer(productFactory),"1号消费者"). start();
        new Thread(new Consumer(productFactory),"2号消费者"). start();
        new Thread(new Consumer(productFactory),"3号消费者"). start();
    }
}

结果验证

线程(1号生产者)生产了一件产品:0;当前剩余商品1个
线程(3号消费者)消费了一件产品:0;当前剩余商品0个
警告:线程(2号消费者)准备消费产品,但当前没有产品
警告:线程(1号消费者)准备消费产品,但当前没有产品
线程(2号生产者)生产了一件产品:0;当前剩余商品1个
线程(2号消费者)消费了一件产品:0;当前剩余商品0个
警告:线程(1号消费者)准备消费产品,但当前没有产品
线程(2号生产者)生产了一件产品:1;当前剩余商品1个
线程(3号消费者)消费了一件产品:1;当前剩余商品0个
线程(1号生产者)生产了一件产品:1;当前剩余商品1个
线程(3号消费者)消费了一件产品:1;当前剩余商品0个
线程(2号生产者)生产了一件产品:2;当前剩余商品1个
线程(1号消费者)消费了一件产品:2;当前剩余商品0个
警告:线程(2号消费者)准备消费产品,但当前没有产品
线程(2号生产者)生产了一件产品:3;当前剩余商品1个
... 
... 

结果分析
从结果来看,生产者线程和消费者线程合作无间,当没有产品时,消费者线程进入等待;当产品达到 10 个最大库存是,生产者进入等待。这就是经典的生产者 - 消费者模型。

6. 小结

实现多线程并发编程中最经典的生产者与消费者模式,这是本节课程的核心内容,所有的知识点都是围绕这一经典模型展开的。 在掌握 synchronized 关键字,wait 方法和 notify 方法的基础上,理解并掌握生产者与消费者模式是本节课程的最终目标。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1885787.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

昇思25天学习打卡营第13天 | SSD目标检测

模型简介 SSD&#xff0c;全称Single Shot MultiBox Detector&#xff0c;是Wei Liu在ECCV 2016上提出的一种目标检测算法。使用Nvidia Titan X在VOC 2007测试集上&#xff0c;SSD对于输入尺寸300x300的网络&#xff0c;达到74.3%mAP(mean Average Precision)以及59FPS&#x…

周界入侵自动监测摄像机

当今&#xff0c;随着科技的快速发展&#xff0c;周界入侵自动监测摄像机作为安全监控领域的重要创新&#xff0c;正逐渐成为各类场所安全防范的核心设备。这种摄像机以其先进的监测和预警功能&#xff0c;有效提升了安全管理的效率和实时响应能力&#xff0c;被广泛应用于各类…

电子看板,帮助工厂实现数字化管理

在数字化浪潮的推动下&#xff0c;制造业正经历着深刻的变革&#xff0c;数字工厂成为了行业发展的新趋势。而生产管理看板作为一种重要的管理工具&#xff0c;在提升数字工厂管理效率方面发挥着关键作用。 生产管理看板通过实时数据的展示&#xff0c;为数字工厂提供了清晰的全…

【微信小程序开发实战项目】——如何制作一个属于自己的花店微信小程序(1)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

Windows10录屏,教你3个方法,简单快速录屏

“我的电脑系统是Windows10的系统&#xff0c;今晚要进行线上开会&#xff0c;但我实在有事没办法参加会议&#xff0c;想把会议的内容录制下来方便我后续观看。但却找不到电脑录屏功能在哪里打开&#xff1f;求助一下&#xff0c;谁能帮帮我&#xff1f;” 在数字化时代&…

软考 有向图 数据库之关系模式范式

假设有一个关系 R(A, B, C, D)&#xff0c;并且已知以下函数依赖&#xff1a; A → B B → C BC → D 求候选键? 求候选码? 候选键/候选码 是同一个概念. 数据库范式也分为1NF,2NF,3NF,BCNF,4NF,5NF。 https://cloud.tencent.com/developer/article/2055118 2NF在1NF的基础…

如何定制化 ListView 界面

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

PostgreSQL主从同步

目录 一、主从复制原理 二、配置主数据库 2.1 创建同步账号 2.2 配置同步账号访问控制 2.3 设置同步参数 3.4 重启主数据库 三、配置从数据库 3.1 停止从库 3.2 清空从库数据文件 3.3 拉取主库数据文件 3.4 配置从库同步参数 3.5 启动从库 四、测试主从 4.1在主库…

33 包装器

c11 也叫适配器。c中的function本质是一个类模板&#xff0c;也是一个包装器 为什么需要fuction呢&#xff1f; 当一个类型既可以是函数指针&#xff0c;也可以是仿函数和lambda比倒是&#xff0c;函数指针的类型不好理解&#xff0c;仿函数写起来麻烦&#xff0c;lambda无法拿…

前端基础:CSS(篇二)

目录 盒子 模型&#xff08;box-model&#xff09; 盒子 模型-内容区 代码 运行 盒子 模型-内边距 代码 运行 盒子 模型-边框 代码 运行 盒子 模型-外边距 代码 运行 清除浏览器的默认样式 代码 运行 盒子模型练习 代码 运行 ​编辑 文档流 浮…

IO模型与多路复用

前言 在Linux中有一句经典台词&#xff1a;“Linux一切皆文件”。IO操作是与文件进行交流的唯一方式&#xff0c;也就是说这是与Linux系统交流的唯一手段。就如同人与人之间的交流&#xff0c;如果我们连交流的方式都不甚了解&#xff0c;交流的效率就会变得低下。操作系统也是…

计算机专业的概念需要拓宽|终身学习之旅利:用FlowUs打造个性化学习记录知识库

计算机相关专业长期以来一直是热门选择&#xff0c;这主要得益于技术的快速发展和广泛的应用场景。随着AI技术的不断进步&#xff0c;这一趋势在未来几年内仍有望持续。以下是从不同角度对这个问题的分析&#xff1a; 从AI发展的角度&#xff1a; 技术革新&#xff1a;AI技术…

2024年地球生态学与绿色发展国际会议 (EEGD 2024)

2024年地球生态学与绿色发展国际会议 (EEGD 2024) International Conference on Earth Ecology and Green Development in 2024 【重要信息】 大会地点&#xff1a;济南 大会官网&#xff1a;http://www.iceegd.com 投稿邮箱&#xff1a;iceegdsub-conf.com 【注意&#xff1a…

新赛季守望先锋2延迟高怎么办?快速降低守望先锋2延迟的小妙招

随着守望先锋2新赛季的开启&#xff0c;这款游戏又引入了大量全新内容&#xff0c;包括新地图、新神话皮肤等。而之前在预告片最后出现的《变形金刚》系列标志性的变形音效&#xff0c;表明在本赛季将与《变形金刚》系列展开联动更是吸引了不少玩家的关注。因为7月9日变形金刚联…

安装Rabbitmq遇到的坑

&#xff01;&#xff01;&#xff01;一定要对号版本号 不同的虚拟机unbontu、cetenos和不同的erlang和不同的rabbitmq之间要对应下载对应版本 下面给出我的版本centos7erlangrabbitmq 分割线 安装好后&#xff0c;如果在虚拟机的服务器上可以打开&#xff0c;在本地浏览器…

【基于R语言群体遗传学】-3-计算等位基因频率

书接上文&#xff0c;我们讲完了哈代温伯格基因型频率&#xff0c;也使用数据进行了拟合&#xff0c;那么接下来就是考虑一些计算的问题&#xff1a; 【基于R语言群体遗传学】-1-哈代温伯格基因型比例-CSDN博客 【基于R语言群体遗传学】-2-模拟基因型&#xff08;simulating …

Hubstudio指纹浏览器:海外代理IP新选择,IPXProxy为何备受推崇?

许多人都会把Hubstudio指纹浏览器和代理IP进行搭配使用&#xff0c;为了保证网络操作的顺利进行&#xff0c;例如亚马逊的多账号管理。那有没有好用的海外代理IP呢&#xff0c;如何在Hubstudio指纹浏览器中使用代理IP呢&#xff1f; 下面就给大家推荐好用的一家海外IP代理&…

收银系统源码-千呼新零售2.0

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

SMARTFORMS

page&#xff08;节点&#xff09;-> wondows(容器)

AI网络爬虫004:从东方财富网批量获取上市公司的全部新闻资讯

文章目录 一、目标二、输入内容三、输出内容一、目标 用户输入一个上市公司名称,然后程序自动从东方财富网批量获取上市公司的全部新闻资讯 查看相关元素在源代码中的位置: 新闻标题:<a href="http://finance.eastmoney.com/a/202405233084538683.html" targ…