JAVA设计模式之生产者-消费模式

2024-08-28 16:48

本文主要是介绍JAVA设计模式之生产者-消费模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、简介

生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程中最常用的一种设计模式。

拿生活中的例子来说,工厂生产出来的产品总是要输出到外面使用的,这就是生产与消费的概念。

在软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。

产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。

二、结构

生产者------》缓冲区-------》消费者

举一个烤肉的例子,假设要去吃烤肉,大致过程如下:

1、烤肉师父会站在一边一直烤肉;

2、再将烤好的肉放在一个盘子中;

3、食客会坐在一边,只要盘子里有肉就会去吃

在这个案例中,生产者----》烤肉师父,缓冲区----》盘子,消费者----》食客负责烤肉。

整个过程中食客与烤肉师父都不是直接打交道的,而是都与盘子进行交互。

烤肉师父烤完了就把肉放在盘子里,盘子充当了一个缓冲区的概念,有东西生产出来就把东西放进去,

盘子也是有大小限制,超过盘子大小就会阻塞生产者生产,等待消费者去消费;当盘子为空的时候 ,即阻塞消费者消费,等待生产者去生产。

编程中阻塞队列(BlockingQueue)即可以实现盘子这个功能。

阻塞队列的特点:

    <1> 当队列元素已满的时候,阻塞插入操作.<2> 当队列元素为空的时候,阻塞获取操作。

设置缓冲区的好处:

<1> 解耦,降低生产者与消费者之间的依赖<2> 支持并发。多线程中由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会一直阻塞。使用了缓冲区之后,生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。

三、例子

下面就是生产者-消费者模式的一个典型的例子:

1、生产者

    public class Producer  implements Runnable{  private volatile  boolean isRunning= true;  //内存缓冲区  private BlockingQueue<PCData> queue;  //总数,原子操作  private static AtomicInteger count = new AtomicInteger();  private static final int SLEEPTIME=1000;  public Producer(BlockingQueue<PCData> queue) {  this.queue = queue;  }  @Override  public void run() {  PCData data=null;  Random r  = new Random();  System.out.println("start producer id = "+ Thread .currentThread().getId());  try{  while(isRunning){  Thread.sleep(r.nextInt(SLEEPTIME));  //构造任务数据  data= new PCData(count.incrementAndGet());  System.out.println("data is put into queue ");  //提交数据到缓冲区  if(!queue.offer(data,2,TimeUnit.SECONDS)){  System.out.println("faile to  put data:  "+ data);  }  }  }catch (InterruptedException e){  e.printStackTrace();  Thread.currentThread().interrupt();  }  }  public void stop(){  isRunning=false;  }   }  	

2、消费者

    public class Consumer implements Runnable {  //缓冲区     private BlockingQueue<PCData> queue;  private static final int SLEEPTIME=1000;  public Consumer(BlockingQueue<PCData> queue) {          this.queue = queue;  }  @Override  public void run() {  System.out.println("start Consumer id= "+ Thread .currentThread().getId());  Random r = new Random();  try {  //提取任务  while(true){  PCData data= queue.take();  if(null!= data){  //计算平方  int re= data.getData()*data.getData();  System.out.println(MessageFormat.format("{0}*{1}={2}", data.getData(),data.getData(),re));  Thread.sleep(r.nextInt(SLEEPTIME));  }  }  } catch (InterruptedException e) {                e.printStackTrace();  Thread.currentThread().interrupt();  }  }  } 

3、PCData共享数据模型:

    public  final class PCData {  private final int intData;  public PCData(int d) {  intData=d;  }  public PCData(String  d) {  intData=Integer.valueOf(d);  }  public int getData(){  return intData;  }  @Override  public String toString(){  return "data:"+ intData ;  }  }  

4、Main函数:

    public class Main {  /** * @param args */  public static void main(String[] args)  throws InterruptedException{  //建立缓冲区  BlockingQueue<PCData> queue = new LinkedBlockingDeque<PCData>(10);  //建立生产者  Producer producer1 = new Producer(queue);  Producer producer2 = new Producer(queue);  Producer producer3 = new Producer(queue);  //建立消费者  Consumer consumer1 = new Consumer(queue);  Consumer consumer2 = new Consumer(queue);  Consumer consumer3 = new Consumer(queue);         //建立线程池  ExecutorService service = Executors.newCachedThreadPool();  //运行生产者  service.execute(producer1);  service.execute(producer2);  service.execute(producer3);  //运行消费者  service.execute(consumer1);  service.execute(consumer2);  service.execute(consumer3);  Thread.sleep(10*1000);  //停止生产者  producer1.stop();  producer2.stop();  producer3.stop();  Thread.sleep(3000);  service.shutdown();  }  }

这篇关于JAVA设计模式之生产者-消费模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1115326

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

javacv依赖太大导致jar包也大的解决办法

《javacv依赖太大导致jar包也大的解决办法》随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,:本文主要介绍javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的... 目录前言1.检查依赖2.更改依赖3.检查副依赖总结 前言最近在写项目时,用到了Javacv里的获取视频

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC