阻塞队列-生产者消费者模型

    • 阻塞队列介绍
      • 标准库阻塞队列使用
      • 基于阻塞队列的简单生产者消费者模型。
      • 实现一个简单型阻塞队列 (基于数组实现)

阻塞队列介绍

不要和之前学多线程的就绪队列搞混;
在这里插入图片描述
阻塞队列:也是一个队列,先进先出。带有特殊的功能 ;阻塞
1:如果队列为空,执行出队列操作,就会阻塞阻塞到另一个线程往队列里添加元素(队列不空)为止
2:如果队列满了,执行入队列操作,也会阻塞阻塞到另一个线程从队列取走元素位置(队列不满)

消息队列:
先简单介绍消息队列:特殊的队列,相当于在阻塞队列的基础上,加上一个消息类型,按照类别进行先进先出。
因为这个消息队列使用起来太香;所以有大佬把这样的数据结构单独实现成一个程序;可以单独的部署到一组服务器上。使储存、转发能力大大提升。现在已经发展可以和mysql、redis相提并论的一个重要组件。
消息队列之所以这么好用和阻塞队列阻塞特性关系非常大;基于这种特性可以实现 “生产者消费者模型”

生产者消费者模型(常用的并发设计模式):比如包饺子;一个人负责制作饺子皮;一个人负责包;一个人负责下锅。不会说每个人单独包一个饺子,这样会导致撵面杖不够用,影响效率。两个好处。
1:实现发送方与接收方之间的解耦。
比如下面这种是耦合度比较高;A要调用B(A把请求转发给B处理,B把结果反馈给A),A得知道B的存在。如果B挂了,很容易引起A的bug。。如果你要增加一个C服务器,对A也需要修改代码,对A又得重新测试等又不知道是否改动A会对B有影响。
在这里插入图片描述

在这里插入图片描述
这种模型下;A、B耦合就降低很多;A中无B,B中无A的代码。两边有一方挂了都互相没任何影响;因为阻塞队列是正常的。B挂了,A仍然可以插入元素,满了就阻塞。A挂了,B仍然可以从队列获取元素;空了就阻塞。增加一个C服务器对A是无感知的。

2:削峰填谷的作用,保证系统的稳定性。
服务器开发也很类似;说不定坤哥唱一首只因你太美;热搜瞬间上来,很多用户给你发请求,如果没有削峰填谷的准备服务器很容易就挂掉。

标准库阻塞队列使用

在这里插入图片描述

Queue提供:入队列 offer 、出队列 poll、取队首元素 peek。
阻塞队列主要方法:入队列 put、出队列 take。抛异常阻塞得需要唤醒,空队列你再取就阻塞。阻塞队列也有offer和poll方法,但是这些是不带阻塞功能的。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.PriorityBlockingQueue;public class test14 {//标准库阻塞队列使用public static void main(String[] args) throws InterruptedException {
//        BlockingDeque<String> q=new PriorityBlockingQueue<>() 基于堆;带有优先级阻塞队列BlockingDeque<String> q=new LinkedBlockingDeque<>();//基于链表实现
//        BlockingDeque<String> q=new ArrayBlockingQueue<>(); 基于数组实现q.put("hello");System.out.println(q.take());//输出结果helloq.take();//空的时候取就会阻塞}
}

基于阻塞队列的简单生产者消费者模型。

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;public class test15 {//基于阻塞队列生产者消费者模型static   int i=0;public static void main(String[] args) {BlockingDeque<Integer> blockingDeque=new LinkedBlockingDeque();Thread t1=new Thread(()->{//生产者while (true){try {blockingDeque.put(i);System.out.println(i);//生产的元素i++;Thread.sleep(500);//生产的很慢;消费者就得阻塞先;等到元素生产出来} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();Thread t2=new Thread(()->{//消费者while (true){try {System.out.println(blockingDeque.take());//消费的元素} catch (InterruptedException e) {throw new RuntimeException(e);}}});t2.start();}
}

执行效果
在这里插入图片描述

实现一个简单型阻塞队列 (基于数组实现)

先复习一下循环队列的实现。

public class test16 {//简单阻塞队列的实现;;先复习一遍循环队列private int[] elem;public int front;//队头public int rear;//队尾public test16(int k) {//创建这个对象,就有循环队列k大小数组elem=new int[k];}//判满;入队的前提条件,入队是满就直接返回。或者你在满的时候扩容,入之前判断是否满;如果满扩容再入public boolean isfull(){//怎么判;队尾+1 回到头。但是刚好队头在原点就会产生;位置相同;但是队尾+1的值不等于0.if((rear+1)%elem.length==front){return true;}return false;}//判空;出队的前提条件,出队是空直接返回public boolean isnull(){//对头==队就是空if(rear==front){return true;}
return false;}//入队方法;
public boolean offer1(int i) {if(isfull()){//满的情况返回入队失败return false;}//怎么入;是入在后面还是前面呢;队列尾入;这时候浪费的那个空间直接赋值进去;队尾往后移就好了elem[rear]=i;//会不会有点不合适;万一前面是没有元素呢rear=(rear+1)%elem.length;                  //这个队尾两种情况;第一种是单纯的加1;未到数组最后位置//比如我把前面空间删除。刚好到最后一个位置;就得回归原点。%elem.length//这里分开写效果会更好;rear++; if(rear>=elem.length){ rear=0; }//相比之下rear++;read=read%elem.length 。这个不易读,不直观;每次得理解好一会 。效率上又没有优势return true;}//出队方法public Int poll1(){//头出、怎么出呢。感觉不合理;出队应该返回这个元素.所以得先记录if(isnull()){return -1;}int value=front;front=(front+1)%elem.length;//往后移一位;elem.length避免逛了一圈return elem[value];}//获取队首public int  peek1(){if(isnull()){return -1;}return elem[front];}//获取队尾public int  Rear(){if(isnull()){return -1;}//得注意;不能直接返回read-1.因为如果read是原点;那么减1不就变成负一if(rear==0){return elem[elem.length-1];}elsereturn elem[rear-1];}public static void main(String[] args) {}
}

我们在此基础上加上阻塞功能就好了;也就是在多线程环境下使用;得保证线程安全。
在线程里使用这些方法时;如果出现符合阻塞条件情况就阻塞。

public class test17 {static int i;public static void main(String[] args) throws InterruptedException {//看看能不能基于我们创建这个阻塞队列实现生产者消费者模型my blockingDeque=new my(100);//这里浪费一个空间;达到98;就会阻塞Thread t1=new Thread(()->{//生产者while (true){try {blockingDeque.offer1(i);System.out.println(i);//生产的元素i++;Thread.sleep(50);//生产的很慢;消费者就得阻塞先;等到元素生产出来} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();Thread t2=new Thread(()->{//消费者while (true){try {System.out.println(blockingDeque.poll1());//消费的元素} catch (InterruptedException e) {throw new RuntimeException(e);}}});// t2.start();}}class my {//简单阻塞队列的实现;;先复习一遍循环队列private int[] elem;public int front;//队头public int rear;//队尾public my(int k) {elem = new int[k];}public void offer1(int i) throws InterruptedException {synchronized (this) {if (isfull()) {this.wait();}elem[rear] = i;rear = (rear + 1) % elem.length;// 这个 notify 唤醒 poll1 中的 waitthis.notify();}}//出队方法public int poll1() throws InterruptedException {synchronized (this) {if (isnull()) {this.wait();//两个wait是不可能同时触发的;因为一个队列不可能既是空又是满。只要this是同一个对象就不会.}int value = front;front = (front + 1) % elem.length;// 这个 notify 唤醒 offer1 中的 waitthis.notify();return elem[value];}}public boolean isfull() {if ((rear + 1) % elem.length ==front) {return true;}return false;}public boolean isnull() {//队头==队尾就是空if (rear == front) {return true;}return false;}}

上述代码还有一丝丝的瑕疵;offer1中wait被唤醒的时候;if的条件一定就不成立?(也就是队列一定是不满的?)
我们当前代码是取元素的时候;就唤醒,是不存在这个问题。万一其它操作也可能唤醒这个wait;但是情况又是队列不满的呢?
所以我们改成

whlie (isnull()) {this.wait();}

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

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

相关文章

PostgreSQL 技术内幕(十)WAL log 模块基本原理

事务日志是数据库的重要组成部分&#xff0c;记录了数据库系统中所有更改和操作的历史信息。 WAL log(Write Ahead Logging)也被称为xlog&#xff0c;是事务日志的一种&#xff0c;也是关系数据库系统中用于保证数据一致性和事务完整性的一系列技术&#xff0c;在数据库恢复、高…

spring复习:(61)自定义CustomAutowireConfigurer

一、自定义注解&#xff1a; package cn.edu.tju.anno;import java.lang.annotation.*;Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Inherited Documented public interface MyQualifier …

若依前后端分离版搭建记录

一、如果是mysql8&#xff0c;得修改一下参数allowPublicKeyRetrieval为true&#xff0c;不然会报Public Key Retrieval is not allowed错误&#xff1a; 二、导入第二张表的数据库的时候&#xff0c;需要增加“--default-character-setutf8”参数才不会报错&#xff1a;

硕士应聘大专老师

招聘信息 当地人社局、学校&#xff08;官方&#xff09; 公众号&#xff08;推荐&#xff09;&#xff1a; 辅导员招聘 厦门人才就业信息平台 高校人才网V 公告出完没多久就要考试面试&#xff0c;提前联系当地院校&#xff0c;问是否招人。 校招南方某些学校会直接去招老师。…

DL2:A Deep Learning-Driven Scheduler for Deep Learning Clusters 阅读思考+组会

IEEE Transactions on Parallel and Distributed Systems CCF A 提问题记录 1、自己提出的问题 1.1 这篇文章解决的主要问题是什么&#xff0c;怎么理解&#xff1f; 传统的调度方法具有如下特点&#xff1a; 要么无法感知机器学习工作负载特性(如集群中的GPU的利用率)或ML…

文章投稿经历

1、卡时间 2、收费高、不收费的SCI2区以上的 3、计算机类的投文章确实难 因为快毕业了&#xff0c;考虑到时间原因找了几周的期刊&#xff0c;一直没投&#xff0c;结果投了一篇&#xff0c;马上就给拒了&#xff0c;应该是好的结果吧&#xff0c;能尽快投下一个 informatio…

嵌入式C++总结

1、new delete与malloc free区别 new delete是运算符&#xff0c;malloc free是函数。 前者不需要传入大小&#xff0c;后者需要。 前者会调用构造、析构函数&#xff0c;后者不会。 前者不需要强制转换&#xff0c;后者需要。 2、智能指针 智能指针是避免忘记释放动态申请对象…

Vue中的插槽--组件复用,内容自定义

插槽 文章目录 插槽插槽-默认插槽插槽-后备内容&#xff08;设置默认值&#xff09;插槽-具名插槽插槽–作用域插槽 插槽-默认插槽 作用&#xff1a;让组件内部的一些结构支持自定义 需求&#xff1a;要在页面中显示一个对话框,封装成一个组件&#xff08;对话框有很多功能是类…

antd/fusion表格增加圈选复制功能

背景介绍 我们存在着大量在PC页面通过表格看数据业务场景&#xff0c;表格又分为两种&#xff0c;一种是 antd / fusion 这种基于 dom 元素的表格&#xff0c;另一种是通过 canvas 绘制的类似 excel 的表格。 基于 dom 的表格功能丰富较为美观&#xff0c;能实现多表头、合并…

Spring之bean的生命周期源码解析

Spring最重要的功能就是帮助程序员创建对象&#xff08;也就是IOC&#xff09;&#xff0c;而启动Spring就是为创建Bean对象做准备&#xff0c;所以我们先明白Spring到底是怎么去创建Bean的&#xff0c;也就是先弄明白Bean的生命周期。 Bean的生命周期就是指&#xff1a;在Spr…

Nodejs 相关知识

Nodejs是一个js运行环境&#xff0c;可以让js开发后端程序&#xff0c;实现几乎其他后端语言实现的所有功能&#xff0c;能够让js与其他后端语言平起平坐。 nodejs是基于v8引擎&#xff0c;v8是Google发布的开源js引擎&#xff0c;本身就是用于chrome浏览器的js解释部分&#…

帆软FineReport决策报表之页面布局

最近在用帆软决策报表绘制首页大屏&#xff0c;记录使用过程&#xff0c;方便查看。 版本&#xff1a;FineReport10.0 第一步、页面布局 页面布局其实就是组件的排列组合&#xff0c;决策报表主区域body有两种布局方式&#xff1a;自适应布局和绝对布局。 1&#xff09;自适应…

第一百五十三回 如何实现滑动窗口

文章目录 概念介绍实现方法示例代码 我们在上一章回中介绍了自定义组件实现游戏摇杆相关的内容&#xff0c;本章回中将介绍 如何实现滑动窗口.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在本章回中介绍的滑动窗口表示在屏幕底部向上滑动时弹出一个窗口&a…

vue返回上一页(后退)的几种方法与区别

从a页面>b页面>c页面&#xff0c;当前在c页面&#xff0c;执行某方法后可以如同按了浏览器后退键一样返回b页面 若项目使用vue-router&#xff0c;this.$router.go(-1)可以回到上一页 this.$router.back() window.history.go(-1) 区别 go(-1): 原页面表单中的内容会丢…

问界M7的诸多优点(自动驾驶走进我们的生活二)

博主一直在问界工厂工作&#xff0c;从未对自己工厂的车如此关注过&#xff1b;但问界系列上市后&#xff0c;经常在茶余饭后看B站视频&#xff0c;发现问界车越来越多不可比拟的优点如下&#xff1a; 一、绿牌 绿牌特权在重庆可以随时过桥&#xff0c;不受限号限制。 二、增…

uLua:在AVR 8位微控制器上运行的高效Lua编译器与迭代器详细指南

1. 介绍 在嵌入式系统领域&#xff0c;资源的限制常常是一个巨大的挑战。微控制器&#xff0c;特别是那些只有1k RAM的微控制器&#xff0c;对于运行大多数现代编程语言来说&#xff0c;资源是非常有限的。但是&#xff0c;有了uLua&#xff0c;这一切都变得可能。uLua是一个专…

【Unity3D赛车游戏制作】开始界面场景搭建

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

SpringSecurity授权

目录 一、RABC的介绍 二、权限表设计 三、编写权限控制方法 &#xff08;1&#xff09;mapper接口 &#xff08;2&#xff09;映射文件 &#xff08;3&#xff09;修改认证逻辑 四、配置类访问资源 五、自定义访问控制逻辑 &#xff08;1&#xff09;自定义 &#xf…

LeakCanary 源码详解(3)

上一篇&#xff1a;LeakCanary源码详解&#xff08;2&#xff09; 如果你是直接刷到这篇的&#xff0c;建议还是从1开始看&#xff0c;然后2&#xff0c;然后是这篇3&#xff0c;如果你只关注这篇的重点hprof 文件定位泄漏位置的感兴趣&#xff0c;可以试试直接读这篇&#xff…

23种设计模式

目录 一、设计模式学前导读 1、代码质量好坏如何评价 ? 2、编程方法论 二、设计模式概述 设计模式的产生 UML图 三、六大设计原则 1、单一职责原则&#xff08;Single Responsibitity Principle&#xff09; 2、开放封闭原则&#xff08;Open Close Principle&#x…