生产者消费者

news2024/11/17 9:49:00

前言


生产者消费者模式属于一种经典的多线程协作的模式,弄清生产者消费者问题能够让我们对于多线程编程有更深刻的理解,下面,为大家分享一个生产者消费者的案例。

一、案例描述
这里以快递为例,假设有一个快递柜,用来存快递,然后有快递员和取件人,快递员往快递柜里存快递,取件人从快递柜中取走快递。快递员作为生产者,取件人作为消费者,当两者在一个时间段同时进行多次自己的操作时,很明显这就是多线程编程的生产者消费者实例了。在这里,我们希望快递员(生产者)存入一个快递,取件人(消费者)就拿走一个快递,如果快递还没有被取走,那么生产者应该等待,而如果快递柜里没有快递,则消费者应该等待。

首先来明确一下,这个案例我们需要准备:

快递柜类(Box):包含一个成员变量,表示快递的序号,并提供存快递和取快递的操作方法
生产者类(Producer):实现Runnable接口,包含存快递的方法
消费者类(Customer):实现Runnable接口,包含取快递的方法
测试类(BoxDemo):测试类按如下步骤实现这个案例
(1) 创建快递柜对象作为共享数据区域
(2) 创建生产者,把快递柜对象作为参数传递至构造方法,因为生产者需要完成存快递的操作
(3)创建消费者,把快递柜作为对象传递至构造方法,因为消费者需要完成取快递的操作
(4)创建两个线程,将生产者和消费者对象分别作为参数传递至线程的构造方法,然后启动线程
下面是具体实现:

二、创建快递柜


代码如下:
————————————————

public class Box {
    //定义成员变量表示第几个快递(快递序号)
    private int express;
    //定义一个成员变量用于表示快递柜的状态
    private boolean flag = false;

    //存快递
    public synchronized void put(int express) {
        //如果有快递,那么快递员应该等待取件人来取快递
        if (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有快递,那么快递员就存入快递
        this.express = express;
        System.out.println("快递员将第" + this.express + "个快递存入了快递柜");
        //别忘了存完修改快递柜的状态
        flag = true;
        //修改完快递柜状态后,唤醒其他在等待的线程
        notifyAll();
    }

    //取快递
    public synchronized void get() {
        //如果有快递,那么取件人就取走快递
        if (flag) {
            System.out.println("取件人取出了第" + this.express + "个快递");
            flag = false;
            notifyAll();
        } else {
            //没有快递,那么取件人就等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

说明:

如之前的分析,我们创建了一个Box类当做快递柜,除了表示快递序号的成员变量以外和对应的存快递、取快递方法外,还包括一个用来标记快递柜状态的变量,因为线程执行时需要这个标记来判断是该执行还是等待。存快递和取快递的方法都加上了sychronized变成了同步方法,因为用于等待的wait()方法和唤醒的notifyAll()方法要在sychronized块中使用,否则会抛出 IllegalMonitorStateException异常而无法执行。

三、创建生产者类

代码如下:
————————————————
 

public class Producer implements Runnable{
    private Box b;

    public Producer(Box b){
        this.b = b;
    }
    @Override
    public void run() {
        for(int i = 1 ;i<11;i++){
            b.put(i);
        }
    }
}

说明:

快递员当做生产者类,它实现了Runnable接口,重写了run()方法,并且有一个Box类型的成员变量,和一个以这个成员变量为参数的构造方法,因为在这个类中要调用存快递的操作。在这里,run()方法里一共存入了10次快递。

四、创建消费者类

代码如下:

public class Customer implements Runnable{
    private Box b ;

    public Customer(Box b){
        this.b = b;
    }
    @Override
    public void run() {
        while(true){
            b.get();
        }
    }
}

说明:

同生产者类一样,消费者(取件人)类也实现了Runnable接口,重写了run()方法,同样有一个Box类型的成员变量,和一个以这个成员变量为参数的构造方法,因为这个类里会调用取快递的操作。由于能取快递的次数是由生产者(快递员)存入多少快递决定的,所以这里我们直接用while循环就好了。

五、测试类


在测试类中,我们分别创建快递柜、生产者和消费者的对象,将快递柜对象作为参数分别传入生产者和消费者创建时的构造方法。然后创建两个线程,分别将生产者和消费者对象作为构造方法的参数传递,最后启动线程,观察结果。

代码如下:
 

public class BoxDemo {
    public static void main(String[] args) {
        //创建快递柜对象
        Box box = new Box();
        //创建生产者和消费者对象
        Producer p = new Producer(box);
        Customer c = new Customer(box);
        //创建两个线程
        Thread t1 = new Thread(p,"生产者线程");
        Thread t2 = new Thread(c,"消费者线程");
        //启动线程
        t1.start();
        t2.start();
    }
}

在这里插入图片描述

 可以看到,快递员和取件人有序地完成了10个快递的存和取

// 店员类:负责进货和售货
class Clerk{
    private int num = 0; //店里当前的货物量

    public synchronized void get() { //店员进货  每次进货一个(生产者)
        if(num >= 10) {
            System.out.println(Thread.currentThread().getName()+" 库存已满,无法进货");
            try {
                this.wait();
                System.out.println(Thread.currentThread().getName()+" wait后剩余步骤");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(Thread.currentThread().getName()+" : "+ (++num));
            this.notifyAll();
        }
    }

    public synchronized void sale() { //店员卖货 每次卖掉一个货(消费者)
        if(num<=0) {
            System.out.println(Thread.currentThread().getName()+" 库存已空,无法卖货");
            try {
                this.wait();
                System.out.println(Thread.currentThread().getName()+" wait后剩余步骤");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println(Thread.currentThread().getName()+" : "+(--num));
            this.notifyAll();
        }
    }
}

// 生产者 可以有很多生产者卖货给这个店员
class Producer implements Runnable{
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk=clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i<20; i++) {
            clerk.get();
        }
    }
}

//消费者:可以很多消费者找店员买货
class Consumer implements Runnable{
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk=clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i<20; i++) {
            clerk.sale();
        }
    }
}

public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();

        Producer producer=new Producer(clerk);
        Consumer consumer=new Consumer(clerk);

        new Thread(producer,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}

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

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

相关文章

蚂蚁链发布零知识证明技术架构 可满足数据“隐私保护”、“可验证”双要求

7 月 8 日&#xff0c;在 2023WAIC 全球区块链产业高峰论坛上&#xff0c;蚂蚁链宣布隐私协作平台 AntChain FAIR 进行全新架构升级&#xff0c;引入零知识证明&#xff08;ZKP&#xff09;为核心的可验证计算技术&#xff0c;从可信数据流转拓展到计算过程、数据属性以及身份的…

Vmware环境下的CentOS安装

CentOS7 下载安装 因为 centos 是安装在 VMware 上面的&#xff0c;所以需要提前安装 VMware centos 下载 网址&#xff1a;http://isoredirect.centos.org/centos/7/isos/x86_64/ 镜像源&#xff1a;http://centos.mirror.rafal.ca/7.9.2009/isos/x86_64/ 安装 centos 创建…

哪些软件分析工具需要使用到pdb符号文件?

目录 1、什么是pdb文件&#xff1f;pdb文件有哪些用途&#xff1f; 2、pdb文件的时间戳与pdb文件名称 3、常用软件分析工具有哪些&#xff1f; 4、使用Windbg调试器查看函数调用堆栈时需要加载pdb文件 4.1、给Windbg设置pdb文件路径 4.2、为什么要设置系统库pdb文件下载服…

深度剖析线上应用节点流量隔离技术

作者&#xff1a;谢文欣&#xff08;风敬&#xff09; 为什么要做流量隔离 源于一个 EDAS 客户遇到的棘手情况&#xff1a;他们线上的一个 Pod CPU 指标异常&#xff0c;为了进一步诊断问题&#xff0c;客户希望在不重建此 Pod 的情况下保留现场&#xff0c;但诊断期间流量还…

Element-UI 实现动态增加多个输入框并校验

文章目录 前言实现通过按钮动态增加表单并验证必填实现动态多个输入框为行内模式&#xff0c;其它为行外模式 前言 在做复杂的动态表单&#xff0c;实现业务动态变动&#xff0c;比如有一条需要动态添加的el-form-item中包含了多个输入框&#xff0c;并实现表单验证&#xff0…

非线性激活函数

目录 理论介绍 常见的激活函数 A. sigmoid函数 B. tanh C.ReLu Leaky Relu 函数 Parametric ReLU (PReLU) Exponential Linear Unit (ELU) 实验结果及分析 理论介绍 在神经网络的计算中&#xff0c;无非就是矩阵相乘&#xff0c;输入的是线性&#xff0c;不论输出层有…

如何修复ssh漏洞进行版本升级

目录 一、ssh低版本漏洞信息 OpenSSH GSSAPI 处理远端代码执行漏洞 OpenSSH GSSAPI认证终止信息泄露漏洞 OpenSSH X连接会话劫持漏洞 二、升级ssh版本进行修复漏洞 第一步 安装Telnet服务 第二步 重启服务 第三步 安装依赖环境 第四步 备份ssh老版本文件 第五步 导入…

【JavaEE进阶】Spring 创建与使用

Spring 创建与使用 1&#xff0c;Spring项目的创建 使用Maven方式来创建一个Spring项目&#xff0c;创建Spring项目和Servlet类似&#xff0c;总共分为以下3步&#xff1a; 创建一个普通Maven项目添加 Spring 框架⽀持&#xff08;spring-context、spring-beans&#xff09;添…

UE特效案例 —— 魔法翅膀

一&#xff0c;环境配置 创建默认地形Landscape&#xff0c;如给地形上材质需确定比例&#xff1b;添加环境主光源DirectionalLight&#xff0c;设置相应的强度和颜色&#xff1b;PostProcessVolume设置曝光&#xff0c;设置Min/Max Brightness为1&#xff1b; 与关闭Game Sett…

【二分查找】35. 搜索插入位置

35. 搜索插入位置 解题思路 使用二分查找算法当找到元素之后直接返回位置即可当没找到元素&#xff0c;将该元素插入到left位置即可 class Solution {public int searchInsert(int[] nums, int target) {// 二分查找int left 0;int right nums.length - 1;while(left < …

数组扁平化flat方法的多种实现

flat() let arr [[1],[2, 3],[4, 5, 6, [7, 8, [9, 10, [11]]]],12 ];// 参数指要提取嵌套数组的结构深度&#xff0c;默认值为 1。 // Infinity 指递归嵌套的所有层级。 let flattedArr arr.flat(Infinity); console.log(flattedArr);执行效果&#xff1a; toString() 注意…

FreeRTOS ~(六)信号量 ~ (2/3)信号量解决互斥缺陷

前情提要 FreeRTOS ~&#xff08;四&#xff09;同步互斥与通信 ~ &#xff08;2/3&#xff09;互斥的缺陷 FreeRTOS ~&#xff08;五&#xff09;队列的常规使用 ~ &#xff08;2/5&#xff09;队列解决互斥缺陷 举例子说明&#xff1a;利用信号量解决前述的"互斥的缺陷&…

SQL Server数据库 -- 表的高级查询

文章目录 一、子查询 嵌套子查询相关子查询二、查询运算 并运算union交运算intersect差运算except三、函数的使用 if语句while语句case语句四、总结 前言 高级子查询是对查询更灵活的运用&#xff0c;学会了高级查询将对数据库使用有很大的帮助。 一、子查询 1、子查询简介 在…

MATLAB画等深度构造图

clc;clear;close all; data xlsread(J_UNCONFORMITY等深度.xlsx); x data(:,1) xmax max(x); xmin min(x); y data(:,2) ymax max(y); ymin min(y); z data(:,3); N 45…

天天刷题-->LeetCode(两数相加)

个人名片&#xff1a; &#x1f405;作者简介&#xff1a;一名大二在校生&#xff0c;热爱生活&#xff0c;爱好敲码&#xff01; \ &#x1f485;个人主页 &#x1f947;&#xff1a;holy-wangle ➡系列内容&#xff1a; &#x1f5bc;️ tkinter前端窗口界面创建与优化 &…

MachineLearningWu_7+8_LogisticRegression/Classification

x.1 logistic regression 对于诸如分辨细胞是恶性肿瘤细胞与否的类似分类问题&#xff0c;我们使用Linear Regression的模型并不合适&#xff0c;所以引入Logistic Regression的模型&#xff0c;并绘制decision boundary&#xff0c;如下&#xff0c; Classification的最后一层…

mysql多表查询内连接,左外连接,排序,having

多表连接查询 use mydb3; -- 创建部门表 create table if not exists dept3(deptno varchar(20) primary key , -- 部门号name varchar(20) -- 部门名字 );– 创建员工表 create table if not exists emp3(eid varchar(20) primary key , -- 员工编号ename varchar(20), -- 员…

python接口自动化(二十二)--unittest执行顺序隐藏的坑(详解)

简介 大多数的初学者在使用 unittest 框架时候&#xff0c;不清楚用例的执行顺序到底是怎样的。对测试类里面的类和方法分不清楚&#xff0c;不知道什么时候执行&#xff0c;什么时候不执行。虽然或许通过代码实现了&#xff0c;也是稀里糊涂的一知半解&#xff0c;这样还好&am…

基于深度学习的高精度Caltech行人检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度Caltech数据集行人检测识别系统可用于日常生活中或野外来检测与定位行人目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的行人目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv…