java-生产者消费者

目录

    • 1.生产者消费者
      • 1.1生产者和消费者模式概述【应用】
      • 1.2生产者和消费者案例【应用】
      • 1.3生产者和消费者案例优化【应用】
      • 1.4阻塞队列基本使用【理解】
      • 1.5阻塞队列实现等待唤醒机制【理解】

1.生产者消费者

1.1生产者和消费者模式概述【应用】

  • 概述

    生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。

    所谓生产者消费者问题,实际上主要是包含了两类线程:

    ​ 一类是生产者线程用于生产数据

    ​ 一类是消费者线程用于消费数据

    为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库

    生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为

    消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

  • Object类的等待和唤醒方法

    方法名说明
    void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
    void notify()唤醒正在等待对象监视器的单个线程
    void notifyAll()唤醒正在等待对象监视器的所有线程

1.2生产者和消费者案例【应用】

  • 案例需求

    • 桌子类(Desk):定义表示包子数量的变量,定义锁对象变量,定义标记桌子上有无包子的变量

    • 生产者类(Cooker):实现Runnable接口,重写run()方法,设置线程任务

      1.判断是否有包子,决定当前线程是否执行

      2.如果有包子,就进入等待状态,如果没有包子,继续执行,生产包子

      3.生产包子之后,更新桌子上包子状态,唤醒消费者消费包子

    • 消费者类(Foodie):实现Runnable接口,重写run()方法,设置线程任务

      1.判断是否有包子,决定当前线程是否执行

      2.如果没有包子,就进入等待状态,如果有包子,就消费包子

      3.消费包子后,更新桌子上包子状态,唤醒生产者生产包子

    • 测试类(Demo):里面有main方法,main方法中的代码步骤如下

      创建生产者线程和消费者线程对象

      分别开启两个线程

  • 代码实现

    public class Desk {//定义一个标记//true 就表示桌子上有汉堡包的,此时允许吃货执行//false 就表示桌子上没有汉堡包的,此时允许厨师执行public static boolean flag = false;//汉堡包的总数量public static int count = 10;//锁对象public static final Object lock = new Object();
    }public class Cooker extends Thread {
    //    生产者步骤:
    //            1,判断桌子上是否有汉堡包
    //    如果有就等待,如果没有才生产。
    //            2,把汉堡包放在桌子上。
    //            3,叫醒等待的消费者开吃。@Overridepublic void run() {while(true){synchronized (Desk.lock){if(Desk.count == 0){break;}else{if(!Desk.flag){//生产System.out.println("厨师正在生产汉堡包");Desk.flag = true;Desk.lock.notifyAll();}else{try {Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}
    }public class Foodie extends Thread {@Overridepublic void run() {
    //        1,判断桌子上是否有汉堡包。
    //        2,如果没有就等待。
    //        3,如果有就开吃
    //        4,吃完之后,桌子上的汉堡包就没有了
    //                叫醒等待的生产者继续生产
    //        汉堡包的总数量减一//套路://1. while(true)死循环//2. synchronized 锁,锁对象要唯一//3. 判断,共享数据是否结束. 结束//4. 判断,共享数据是否结束. 没有结束while(true){synchronized (Desk.lock){if(Desk.count == 0){break;}else{if(Desk.flag){//有System.out.println("吃货在吃汉堡包");Desk.flag = false;Desk.lock.notifyAll();Desk.count--;}else{//没有就等待//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.try {Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}
    }public class Demo {public static void main(String[] args) {/*消费者步骤:1,判断桌子上是否有汉堡包。2,如果没有就等待。3,如果有就开吃4,吃完之后,桌子上的汉堡包就没有了叫醒等待的生产者继续生产汉堡包的总数量减一*//*生产者步骤:1,判断桌子上是否有汉堡包如果有就等待,如果没有才生产。2,把汉堡包放在桌子上。3,叫醒等待的消费者开吃。*/Foodie f = new Foodie();Cooker c = new Cooker();f.start();c.start();}
    }
    

1.3生产者和消费者案例优化【应用】

  • 需求

    • 将Desk类中的变量,采用面向对象的方式封装起来
    • 生产者和消费者类中构造方法接收Desk类对象,之后在run方法中进行使用
    • 创建生产者和消费者线程对象,构造方法中传入Desk类对象
    • 开启两个线程
  • 代码实现

    public class Desk {//定义一个标记//true 就表示桌子上有汉堡包的,此时允许吃货执行//false 就表示桌子上没有汉堡包的,此时允许厨师执行//public static boolean flag = false;private boolean flag;//汉堡包的总数量//public static int count = 10;//以后我们在使用这种必须有默认值的变量// private int count = 10;private int count;//锁对象//public static final Object lock = new Object();private final Object lock = new Object();public Desk() {this(false,10); // 在空参内部调用带参,对成员变量进行赋值,之后就可以直接使用成员变量了}public Desk(boolean flag, int count) {this.flag = flag;this.count = count;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}public Object getLock() {return lock;}@Overridepublic String toString() {return "Desk{" +"flag=" + flag +", count=" + count +", lock=" + lock +'}';}
    }public class Cooker extends Thread {private Desk desk;public Cooker(Desk desk) {this.desk = desk;}
    //    生产者步骤:
    //            1,判断桌子上是否有汉堡包
    //    如果有就等待,如果没有才生产。
    //            2,把汉堡包放在桌子上。
    //            3,叫醒等待的消费者开吃。@Overridepublic void run() {while(true){synchronized (desk.getLock()){if(desk.getCount() == 0){break;}else{//System.out.println("验证一下是否执行了");if(!desk.isFlag()){//生产System.out.println("厨师正在生产汉堡包");desk.setFlag(true);desk.getLock().notifyAll();}else{try {desk.getLock().wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}
    }public class Foodie extends Thread {private Desk desk;public Foodie(Desk desk) {this.desk = desk;}@Overridepublic void run() {
    //        1,判断桌子上是否有汉堡包。
    //        2,如果没有就等待。
    //        3,如果有就开吃
    //        4,吃完之后,桌子上的汉堡包就没有了
    //                叫醒等待的生产者继续生产
    //        汉堡包的总数量减一//套路://1. while(true)死循环//2. synchronized 锁,锁对象要唯一//3. 判断,共享数据是否结束. 结束//4. 判断,共享数据是否结束. 没有结束while(true){synchronized (desk.getLock()){if(desk.getCount() == 0){break;}else{//System.out.println("验证一下是否执行了");if(desk.isFlag()){//有System.out.println("吃货在吃汉堡包");desk.setFlag(false);desk.getLock().notifyAll();desk.setCount(desk.getCount() - 1);}else{//没有就等待//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.try {desk.getLock().wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}
    }public class Demo {public static void main(String[] args) {/*消费者步骤:1,判断桌子上是否有汉堡包。2,如果没有就等待。3,如果有就开吃4,吃完之后,桌子上的汉堡包就没有了叫醒等待的生产者继续生产汉堡包的总数量减一*//*生产者步骤:1,判断桌子上是否有汉堡包如果有就等待,如果没有才生产。2,把汉堡包放在桌子上。3,叫醒等待的消费者开吃。*/Desk desk = new Desk();Foodie f = new Foodie(desk);Cooker c = new Cooker(desk);f.start();c.start();}
    }
    

1.4阻塞队列基本使用【理解】

  • 阻塞队列继承结构

    在这里插入图片描述

  • 常见BlockingQueue:

    ArrayBlockingQueue: 底层是数组,有界

    LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值

  • BlockingQueue的核心方法:

    put(anObject): 将参数放入队列,如果放不进去会阻塞

    take(): 取出第一个数据,取不到会阻塞

  • 代码示例

    public class Demo02 {public static void main(String[] args) throws Exception {// 创建阻塞队列的对象,容量为 1ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);// 存储元素arrayBlockingQueue.put("汉堡包");// 取元素System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take()); // 取不到会阻塞System.out.println("程序结束了");}
    }
    

1.5阻塞队列实现等待唤醒机制【理解】

  • 案例需求

    • 生产者类(Cooker):实现Runnable接口,重写run()方法,设置线程任务

      1.构造方法中接收一个阻塞队列对象

      2.在run方法中循环向阻塞队列中添加包子

      3.打印添加结果

    • 消费者类(Foodie):实现Runnable接口,重写run()方法,设置线程任务

      1.构造方法中接收一个阻塞队列对象

      2.在run方法中循环获取阻塞队列中的包子

      3.打印获取结果

    • 测试类(Demo):里面有main方法,main方法中的代码步骤如下

      创建阻塞队列对象

      创建生产者线程和消费者线程对象,构造方法中传入阻塞队列对象

      分别开启两个线程

  • 代码实现

    public class Cooker extends Thread {private ArrayBlockingQueue<String> bd;public Cooker(ArrayBlockingQueue<String> bd) {this.bd = bd;}
    //    生产者步骤:
    //            1,判断桌子上是否有汉堡包
    //    如果有就等待,如果没有才生产。
    //            2,把汉堡包放在桌子上。
    //            3,叫醒等待的消费者开吃。@Overridepublic void run() {while (true) {try {bd.put("汉堡包");System.out.println("厨师放入一个汉堡包");} catch (InterruptedException e) {e.printStackTrace();}}}
    }public class Foodie extends Thread {private ArrayBlockingQueue<String> bd;public Foodie(ArrayBlockingQueue<String> bd) {this.bd = bd;}@Overridepublic void run() {
    //        1,判断桌子上是否有汉堡包。
    //        2,如果没有就等待。
    //        3,如果有就开吃
    //        4,吃完之后,桌子上的汉堡包就没有了
    //                叫醒等待的生产者继续生产
    //        汉堡包的总数量减一//套路://1. while(true)死循环//2. synchronized 锁,锁对象要唯一//3. 判断,共享数据是否结束. 结束//4. 判断,共享数据是否结束. 没有结束while (true) {try {String take = bd.take();System.out.println("吃货将" + take + "拿出来吃了");} catch (InterruptedException e) {e.printStackTrace();}}}
    }public class Demo {public static void main(String[] args) {ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);Foodie f = new Foodie(bd);Cooker c = new Cooker(bd);f.start();c.start();}
    }
    

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

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

相关文章

Linux软件包管理器yum—1

目录 一、Linux下软件安装的方式 二、yum 2.1查看yum已配置的源&#xff1a; 2.2查看yum配置文件&#xff1a; 2.3使用root用户安装软件&#xff1a; 2.4模糊搜索 2.5安装软件&#xff1a; 2.6卸载软件&#xff1a; 2.7扩展源&#xff1a; 2.8yum源更新&#xff1a; 一…

实验室三大常用仪器2---函数信号发生器的基本使用方法(笔记)

目录 函数信号发生器的基本使用方法 如何连接函数信号发生器和示波器 实验室三大常用仪器1---示波器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客 实验室三大常用仪器3---交流毫伏表的使用方法&#xff08;笔记&#xff09;-CSDN博客 示波器是用来显示和测量信号的…

NLP预训练模型- GPT-3学习指南与学习总结案例

NLP预训练模型GPT-3学习指南与学习案例 学习指南 GPT-3&#xff0c;作为OpenAI开发的一种先进的语言生成模型&#xff0c;具有强大的语言理解和生成能力。为了有效地学习和应用GPT-3&#xff0c;以下是一些建议的学习指南&#xff1a; 理解模型原理&#xff1a;首先&#xf…

05-Logistic Softmax详解

Logistic Regression 本章我们来看另一个简单且强大的解决线性回归二元分类问题的算法&#xff0c; 即逻辑回归。不要望文生义&#xff0c; 逻辑回归虽然带回归这个字眼&#xff0c; 但它其实处理的是分类任务&#xff0c;即逻辑回归是一种分类模型&#xff0c; 而非回归模型&…

Docker(九):MySQL主从复制搭建

一&#xff1a;master 1.1 /mydata/mysql-master/conf/my.conf [mysqld] #同一局域网需要唯一 server_id101 # 不需要同步的数据库 binlog-ignore-dbmysql # 开启二进制日志 log-binmall-mysql-bin # 二进制日志使用内存大小 binlog_cache_size1M # 二进制日志格式 binlog_fo…

西夏区第三届中华诗词大会活动方案

活动流程/比赛规则 1.【13:30-14:10】 参赛选手签到&#xff1b;领取参赛号码牌&#xff1b;分组抽签&#xff1b;拍摄赛前感言&#xff0c;集体祝福口号&#xff1b; 2.【14:10-14:25】 熟悉设备、答题环节、题目设置等&#xff0c;走台演练 3.【14:25-14:30】 播放暖场视频…

程序员如何在副业上突破时间片限制

前面我们讨论了如何优化时间片的使用&#xff0c;敏锐的读者可能已经意识到&#xff0c;实际上有方法可以突破时间片的限制。下面我们将具体探讨如何实现这一点。 倍增 第一个策略称为“倍增”&#xff0c;即成倍增加可用的时间片数量。但这需要在一个前提下进行&#xff1a;不…

怎么配置python

右键点击“计算机”&#xff0c;选择“属性”。 在左侧栏找到“高级系统设置”。 点击“环境变量”。 在系统变量中&#xff0c;双击“Path”。 在字符串的末尾&#xff0c;加一个分号; 然后再输入你安装python的路径&#xff0c;如图所示&#xff1a; 点击“确定”&#xff0…

logisim 图解超前进位加法器原理解释

鄙人是视频作者&#xff0c;文件在视频简介的网盘链接。 找规律图解超前进位加法器与原理解释_哔哩哔哩_bilibili 一句话就是“把能导致进位到这个位置的情况全都穷举一遍。” 穷举情况看图中算式。 视频讲解比较啰嗦。

C++修炼之路之继承<一>隐藏,赋值转换规则,继承关系

目录 前言 一&#xff1a;继承的概念和定义 1.概念 2.继承的定义 1.定义格式 2.继承关系和访问限定符 3.继承基类成员访问方式的变化 二&#xff1a;基类和派生类对象赋值转换 规则 三&#xff1a;继承中的作用域 规则 经典举例 经典例题--区分函数重载和隐藏…

PyQt介绍——QStackedWidget堆栈组件的介绍使用

QStackedWidget是一个堆栈窗口控件&#xff0c;用于管理多个堆叠的子部件。它只显示当前选中的子部件&#xff0c;而隐藏其余的子部件。 例子&#xff1a; ControlWidget窗口中&#xff0c;创建QStackedWidget&#xff0c;分别添加两个组件&#xff0c;为Test1Widget和Test2W…

解决方案:Pandas如何用np.where将值分成两类,及处理异常值更新数据

文章目录 一、现象二、解决方案 一、现象 用SQL将值分成两类&#xff0c;就是使用case when&#xff0c;来进行处理。 我平常用SQL处理数据这方面比较多&#xff0c;但用Pandas处理数据这方面比较少&#xff0c;所以一时犯了难&#xff0c;百度一下解决了&#xff0c;鉴于此&a…

商务品牌解决方案企业网站模板 Bootstrap5

目录 一.前言 二.展示 三.下载链接 一.前言 这个网站包含以下内容&#xff1a; 导航栏&#xff1a;主页&#xff08;Home&#xff09;、关于&#xff08;About&#xff09;、服务&#xff08;Services&#xff09;、博客&#xff08;Blog&#xff09;等页面链接。主页部分…

非我族类,其心必异?

“非我族类&#xff0c;其心必异”出自《左传》&#xff0c;意思是&#xff1a;不是我们同族的人&#xff0c;必定不与我们同一条心。 看起来很有道理&#xff0c;即使是投降、投靠的异族人&#xff0c;甚至被自己本族人深深伤害而怀有仇恨的人&#xff0c;也未必会完全背叛他…

thinkphp 框架封装curl请求

tp6 或者 tp8框架 在框架的app/common.php 文件里加一些方法就可以 app\common.php 在这个文件里加 以下代码 就可以实现基于 curl的请求方法 (记得要开启 php的curl扩展) 查看方法 cmd里输入 php -m if (!function_exists(get)) {/*** 发送get请求* param string $url 请求…

Winfrom —— GDI画板

定义绘制对象 Graphics g; 起始点坐标 Point start; 画笔颜色 Color c1 Color.Black; 是否开始绘制 当flagtrue开始绘制&#xff0c;结束绘 private void Form1_MouseDown(object sender, MouseEventArgs e) {if (e.Button MouseButtons.Left) //点击了鼠标左键{start …

excel表格怎么设置密码?excel文件加密的两个方法

一、加密码的原理​ Excel加密码的原理主要基于加密算法和密钥管理。当用户为Excel文件或工作表设置密码时&#xff0c;Excel会采用一种加密算法对文件或工作表进行加密处理。这种加密算法通常是对称加密算法&#xff0c;如AES(高级加密标准)或DES(数据加密标准)。 二&#x…

【STM32项目】基于STM32与物联网融合效果下智能家居设计(完整工程资料源码)

基于STM32与物联网融合效果下智能家居设计演示效果 智能家居 前言: 基于STM32与物联网融合效果的智能家居设计,正成为现代家庭生活中不可或缺的一部分。这一设计融合了嵌入式系统、网络通信、传感器技术等多领域知识,旨在实现家居环境的智能化和自动化控制,为居民提供更加便…

数据结构—单链表

1、链表的概念及结构 1.1链表的概念 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;但在逻辑上确是连续、顺序的&#xff0c;而数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 1.2链表的结构 如下图&#xff1a; 逻辑上的链表&#xff0c;pList是指…

中断的设备树修改及上机实验(按键驱动)流程

写在前面的话&#xff1a;对于 GPIO 按键&#xff0c;我们并不需要去写驱动程序&#xff0c;使用内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以&#xff0c;然后你需要做的只是修改设备树指定引脚及键值。 根据驱动文件中的platform_driver中的.of_match_tabl…