生产者消费者

news/2024/10/18 0:30:14/

前言


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

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

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

快递柜类(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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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.ppmy.cn/news/798599.html

相关文章

PPT快捷键超全合集,你也可以成为PPT实操王者

大家好我是马力 快捷键是PPT重要的功能&#xff0c;很多专业的PPT达人、设计师&#xff0c;都是在很短的时间内&#xff0c;就可以完成高水准的作品&#xff0c;操作行云流水&#xff0c;其实快捷键的使用就非常的重要。 &#xff08;末尾有有彩蛋&#xff0c;150套免费的PPT模…

PPT技能速成班学习笔记

第一讲 对ppt的理解 ppt做的好的人&#xff0c;一定是站在观众角度思考的人。 审美决定一切。多看。推荐&#xff1a;站酷、花瓣 初始化设置&#xff1a; 1&#xff09;撤销次数&#xff1a;文件——选项——高级——编辑选项——最多可取消操作数&#xff1a;150 2&#xff0…

如何又快又好地写出优质PPT

本人在从业的十多年当中&#xff0c;也需要撰写各种PPT&#xff0c;精心设计过各种场景的PPT&#xff0c;积累了大量的经验与心得&#xff0c;谨以此献给每天都要用幻灯片去说服同事、领导、客户的职场人士们&#xff01; 为什么要做PPT 为什么要做PPT&#xff1f;原因不用多…

GPT-4 太强了!一夜醒来,Excel 到 PPT 动嘴就能做!

梦晨 丰色 发自 凹非寺 量子位 | 公众号 QbitAI 一觉醒来&#xff0c;工作的方式被彻底改变。 微软把AI神器GPT-4全面接入Office&#xff0c;这下ChatPPT、ChatWord、ChatExcel一家整整齐齐。 CEO纳德拉在发布会上直接放话&#xff1a;今天&#xff0c;进入人机交互的新时代&am…

[激光原理与应用-21]:《激光原理与技术》-7- 激光技术大汇总与总体概述

目录 前言&#xff1a; 第1章 什么是激光技术 1.1 什么是激光技术 1.2 激光技术的分类方法 第2章 激光的应用技术 2.1 激光加工技术 2.2 激光快速成型 2.3 激光切割 2.4 激光焊接 2.5 激光雕刻 2.6 激光打孔 2.7 激光蚀刻 2.8 激光手术 2.9 激光武器 2.10 激光能…

在powerpoint中使用激光笔

公司培训时向培训师学习的小技巧。在powerpoint (2010) 播放状态下&#xff0c;ctrl 鼠标左键就会出激光笔。效果如图。

Go语言程序设计(八)数据输入输出

Go语言提供了多种用于实现数据输入、输出的函数,这些函数在fmt包中实现。在输入、输出数据时,首先要导入fmt包,导入语句为: import "fmt" 一、标准输出函数 数据输出(Data Output),是计算机对各类输入数据进行加工处理后,将结果以用户所要求的形式输出到标准输出设…