多线程(初阶七:阻塞队列和生产者消费者模型)

一、阻塞队列的简单介绍

二、生产者消费者模型

三、模拟实现阻塞队列

一、阻塞队列的简单介绍

首先,我们都知道,队列是先进先出的一种数据结构,而阻塞队列,是基于队列,做了一些扩展,在多线程有就非常有意义了

阻塞队列的特性:

(1)是线程安全的

(2)具有阻塞的特性

                ①当队列满了,这时不能往队列里放数据,就会阻塞等待,等队列的数据出队列后,这时队列没满,才能放数据。

                ②当队列空了,这时不能拿队列里的数据,就会阻塞等待,等有数据如队列了,这时队列不为空,才能拿数据。

这里,阻塞队列的用处非常大,基于阻塞队列的功能,就可以实现 “生产者消费者模型”。


二、生产者消费者模型

生产者消费者模型是一种很朴素的概念,描述的是一种多线程编程的方法。

1、举个栗子:

一家人包饺子,首先得和面,和完面就要开始擀饺子皮了,然后才开始包饺子,这里,因为一般家里也只有一个擀面杖,所以只能一个人擀饺子皮,其余的家人就会帮忙包饺子,假设一个人擀饺子皮,2个人包饺子,那么擀饺子皮的人就是生产者,包饺子的人就是消费者;这也就是消费者生产者模型。

一个桌子能发饺子皮的数量是有限的,当擀饺子皮的人比较快,桌子放满饺子皮后,就要等包饺子的人,消耗一些饺子皮来包饺子,才能继续擀饺子皮;而这也类似阻塞队列中队里满的情况。当包饺子的人包的比较快,桌子上的饺子皮都没了,就要等擀饺子皮的人擀了饺子皮后才能继续包饺子;而这也类似阻塞队列空的情况。不同的人分工不同,也类似多线程,各自干各自的事情。

2、引入生产者消费者模型的意义:

(1)解耦合

引入生产者消费者模型,就能更好的做到解耦合(把代码的耦合程度,从高降低,就称为解耦合)

在实际开发中,会涉及到 “分布式系统” ,服务器的整个功能不是又一个服务器完成实现的,而是由多个服务器,各自实现各自的一部分功能,再通过网络通信,把这些服务器联系起来,最终完成整个服务器的功能。粗糙流程如图:

而这时,入口服务器与A、B服务器的联系是密切相关的,请求要经过入口服务器,才能传达给A、B服务器,再A、B服务器拿到想要的数据,再返回给入口服务器,通过入口服务器,再把响应传给客户端。如果是这样,那如果请求突然骤升,这时超过入口服务器接收请求的峰值,这时入口服务器就挂了,入口服务器挂了后,A、B服务器拿不到请求,也会挂掉,这就体现了入口服务器和A、B服务器的耦合性比较高。

当我们在入口服务器和A、B服务器之间引入阻塞队列时,如图:

这时,如果入口服务器挂了,但是阻塞队列中还有请求的数据,至少不会因入口服务器挂了,A、B服务器也挂了,这样,入口服务器和A、B服务器的耦合性也就降低了。

(2)削峰填谷

如图,当客户端这边的请求突然骤增时,入口服务器都是比较能抗压的,但是也是有极限的,这时我们引入阻塞队列,就可以把这些请求数据都放进阻塞队列中,形成一个缓冲区,这样,即使外面的请求达到了峰值,也是由阻塞队列来承担,这样就形成了削峰填谷的效果。

注意:这时的阻塞队列,是基于阻塞队列这一数据结构,而实现的服务器程序,所以也叫消息队列

三、模拟实现阻塞队列

1、阻塞队列的简单介绍

在java标准库中,提供了现成的阻塞队列这一数据结构,如图:

是基于队列扩展而来的,队列有的,它也有;我们知道,入队列时可以用offer方法,出队列时可以用poll方法,在阻塞队列中,也有这两个方法,但是这两个方法是不带阻塞功能的;其中,在阻塞队列中,put是在阻塞功能的入队列,take方法是带阻塞功能的出队列

代码案例:

public class TestDemo1 {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);blockingQueue.put("aaa");String s1 = blockingQueue.take();System.out.println("第一个打印:s1 = " + s1);s1 = blockingQueue.take();System.out.println("第二个打印:s1 = " + s1);}
}

执行结果:

线程卡住不动了,原因是想要第二次出队列时,队列是空的,所以要等队列有元素入队列时,才能出队列,也就是说,这是带有阻塞功能的

2、实现阻塞队列

阻塞队列是通过循环队列实现的,而队列是依靠数组来实现的,这里的阻塞队列,我们只模拟实现其中的put和take方法。

(1)实现普通队列

代码:

// 为了简单, 不写作泛型的形式. 考虑存储的元素就是单纯的 String
class MyBlockingQueue {private String elems[] = null;private int head = 0;//记录头结点private  int tail = 0;//记录尾结点private int size = 0;//队列元素个数//构造方法,定义队列的容量大小public MyBlockingQueue(int capacity) {this.elems = new String[capacity];}//入队列public void put(String elem) {//判断容量满了没,满了就不能入队列,要阻塞等待if(size >= this.elems.length) {//阻塞等待,先不写,先实现普通功能的队列return;}//入队列elems[tail] = elem;tail++;//因为是循环队列,所以要判断尾巴有没有超过容量大小下标,超过了就要从0开始了if(tail > elems.length) {tail = 0;}//队列元素要++size++;}//出队列public String take() {String elem = null;//要判断队列是不是空的,空就不能出队列了,要阻塞等待if(size == 0) {//阻塞等待,因为是先实现普通队列的功能,所以后面再补充return null;}elem = elems[head];head++;//因为是循环队列,所以要判断头结点有没有超过容量大小下标,超过了就要0开始了if(head >= elems.length) {head = 0;}//出队列后,队列元素要--size--;return elem;}
}

每个步骤说明代码中有注释。

测试一下可不可以用,如图:

是可以用的,这样,普通的队列就已经搞好了

(2)加上线程安全

我们想想put和take里面要给哪里上锁,首先,写操作肯定是要加锁的,因为多线程同时执行写操作,肯定是线程不安全的,也就是下面这段代码:

接下来,我们讨论一下这两个代码要不要加锁,以take为例,如图:

我们画一下图,会比较好理解:

如果size = -1,是不符合我们预期的,size最小也只可能是0,不可能是-1,所以我要上面的判断条件也要加锁。

而put也一样,判断条件也要加锁。

代码:

class MyBlockingQueue {Object locker = new Object();private String elems[] = null;private int head = 0;//记录头结点private  int tail = 0;//记录尾结点private int size = 0;//队列元素个数//构造方法,定义队列的容量大小public MyBlockingQueue(int capacity) {this.elems = new String[capacity];}//入队列public void put(String elem) {synchronized (locker) {//判断容量满了没,满了就不能入队列,要阻塞等待if(size >= this.elems.length) {//阻塞等待,先不写,先实现普通功能的队列return;}//因为这些都是写操作,也有读操作,多线程并发执行时,写操作是线程不安全的,要把这些打包成一个原子,加锁synchronized (locker) {//入队列elems[tail] = elem;tail++;//因为是循环队列,所以要判断尾巴有没有超过容量大小下标,超过了就要从0开始了if(tail > elems.length) {tail = 0;}//队列元素要++size++;}}}//出队列public String take() {String elem = null;//因为这些都是写操作,也有读操作,多线程并发执行时,写操作是线程不安全的,要把这些打包成一个原子,加锁synchronized (locker) {//要判断队列是不是空的,空就不能出队列了,要阻塞等待if(size == 0) {//阻塞等待,因为是先实现普通队列的功能,所以后面再补充return null;}elem = elems[head];head++;//因为是循环队列,所以要判断头结点有没有超过容量大小下标,超过了就要0开始了if(head >= elems.length) {head = 0;}//出队列后,队列元素要--size--;return elem;}}
}

(3)加上阻塞功能

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

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

相关文章

【Node.js】笔记整理 3 -npm

写在最前&#xff1a;跟着视频学习只是为了在新手期快速入门。想要学习全面、进阶的知识&#xff0c;需要格外注重实战和官方技术文档&#xff0c;文档建议作为手册使用 系列文章 【Node.js】笔记整理 1 - 基础知识【Node.js】笔记整理 2 - 常用模块【Node.js】笔记整理 3 - n…

Redis--14--BigKey 和 热点Key

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 BigKey1.什么是bigkey2.bigkey的危害3.发现bigkeyscan 4.解决bigkey 什么是热点Key&#xff1f;该如何解决1. 产生原因和危害原因危害 2.发现热点key预估发现客户端…

深入理解贝叶斯分类与朴素贝叶斯模型(Naive Bayes, NB):从基础到实战

目录 贝叶斯分类 公式 决策规则 优点 贝叶斯分类器的例子——垃圾邮件问题 1. 特征&#xff08;输入&#xff09;&#xff1a; 2. 类别&#xff1a; 3. 数据&#xff1a; 4. 模型训练&#xff1a; 注&#xff1a;类别先验概率 5. 模型预测&#xff1a; 朴素贝叶斯模…

基于SSM框架开发的酒店后台管理系统

基于SSM框架开发的酒店后台管理系统 文章目录 基于SSM框架开发的酒店后台管理系统 一.引言二.系统设计三.技术架构四.功能实现五.界面展示六.源码获取 一.引言 酒店管理系统是一个集客房预订、前台管理、客户服务、财务管理等功能于一体的综合性软件系统。它能够帮助酒店高效地…

爬虫-xpath篇

1.xpath的基础语法 表达式描述nodename选中该元素/从根节点选取、或者是元素和元素间的过渡//从匹配选择的当前节点选择文档中的节点&#xff0c;而不考虑它们的位置.选取当前节点…选取当前节点的父节点选取属性text()选取文本 举例&#xff1a; 路径表达式结果html选择html元…

shell编程系列(10)-使用paste拼接列

使用paste拼接列 前言使用paste拼接列拼接两个文件 结语 前言 在前面的文章中讲解了使用cut命令选择列&#xff0c;这篇文章我们介绍使用paste命令拼接列&#xff0c;其实这个命令的使用场景很有限&#xff0c;做科研的同学可能才会用到&#xff0c;但是却非常好用&#xff0c…

游戏被流量攻击会有什么样的影响,该用什么样的防护方式去处理

德迅云安全-领先云安全服务与解决方案提供商德迅云游戏盾专门针对游戏进行防护&#xff0c;可免费提供防护方案~ 如果游戏被流量攻击会产生以下影响&#xff1a; 服务器过载&#xff1a;流量攻击会导致游戏服务器接收到的请求数量急剧增加&#xff0c;超出服务器的处理能力。这…

vue3中如何实现事件总线eventBus

使用插件 由于vue3中 “$ on”&#xff0c;$ off 和 $ once 实例方法已被移除&#xff0c;组件实例不再实现事件触发接口 所以我们可以使用官方推荐的这个第三方库实现同样的效果 mitt https://github.com/developit/mitt 安装 pnpm install mitt -S挂载全局写法 main.ts 初始…

网络编程之套接字

端口 && IP 在学习套接字编程之前&#xff0c;我们必须了解一下前缀知识。首先是IP和端口的作用。 在这之前&#xff0c;我们要明白一件事。那就是把数据从一台主机发送到另一台主机&#xff0c;是目的吗&#xff1f;&#xff1f;&#xff1f;当然不是&#xff01;&a…

socket.io介绍

1. 使用的技术 Socket.IO 是一个封装了 Websocket、基于 Node 的 JavaScript 框架&#xff0c;包含 client 的 JavaScript 和 server 的 Node。其屏蔽了所有底层细节&#xff0c;让顶层调用非常简单。 另外&#xff0c;Socket.IO 还有一个非常重要的好处。其不仅支持 WebSocket…

leetcode:232. 用栈实现队列

一、题目 原题链接&#xff1a;232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 函数原型&#xff1a; typedef struct //我的队列结构定义 { } MyQueue; MyQueue* myQueueCreate() //我的队列创建及其初始化 void myQueuePush(MyQueue* obj, int x) //我的队…

生成对抗网络——研讨会

时隔一年&#xff0c;再跟着李沐大师学习了GAN之后&#xff0c;仍旧没能在离散优化中实现通用的应用&#xff0c;实在惭愧&#xff0c;借着组内研讨会的机会&#xff0c;再队GAN的前世今生做一个简单的综述。 GAN产生的背景 目前与GAN相关的应用 去reddit社区的机器学习板块…

〖大前端 - 基础入门三大核心之JS篇㊺〗- 定时器和延时器

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

基于helm的方式在k8s集群中部署gitlab - 升级(三)

接上一篇 基于helm的方式在k8s集群中部署gitlab - 部署&#xff08;一&#xff09;&#xff0c;本篇重点对gitlab在k8s集群中进行升级 文章目录 1. gitlab 升级1.1 获取release1.2 下载目前版本的gitlab charts1.3 获取当前的values文件1.4 升级 2. gitlab数据库升级2.1 备份数…

力扣题:字符串的反转-11.22

力扣题-11.22 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;541. 反转字符串 II 解题思想&#xff1a;进行遍历翻转即可 class Solution(object):def reverseStr(self, s, k):""":type s: str:type k: int:rtype: str"&quo…

简单搭建Python开发环境

Python环境安装 Python官网: Welcome to Python.org 1. 选择Python3.x版本下载&#xff0c;建议使用稳定版3.9.13&#xff08;Stable Releases&#xff09;&#xff0c;绝大数库对3.9版本Python已良好支持&#xff0c;但对3.10及以上支持不完全&#xff1a; https://www.…

SSM框架(六):SpringBoot技术及整合SSM

文章目录 一、概述1.1 简介1.2 起步依赖1.3 入门案例1.4 快速启动 二、基础配置2.1 三种配置文件方式2.2 yaml文件格式2.3 yaml读取数据方式&#xff08;3种&#xff09; 三、多环境开发3.1 yml文件-多环境开发3.2 properties文件-多环境开发3.3 多环境命令行启动参数设置3.4 多…

2023年腾讯云双12优惠活动整理汇总

2023年双12腾讯云推出了年末感恩回馈活动&#xff0c;年度爆款2核2G4M云服务器118元/年&#xff0c;新老用户同享&#xff0c;还可领取总面值2000元代金券&#xff0c;老用户服务器续费4折起。本文为大家整理汇总腾讯云双12优惠活动。 活动地址&#xff1a; 点此直达腾讯云双1…

JOSEF 快速中间继电器 KZJ-4H-L DC220V 导轨安装

快速中间继电器KZJ-4H-LDC220V导轨安装导轨安装是广泛用于电力系统&#xff0c;能够断货开或开通大负载&#xff0c;并且具有较强的断弧能力&#xff0c;适用于交流50/60Hz。电压24380V,直流电压24280V自动控制电路中以增加保护和控制回路的触点数量与触点容量。 KZJ系列快速中…

“B2B+OMS方案”,赋能家电巨头构建BC订单一体化能力,促进业务增长|徐礼昭

某国际知名家电电器品牌&#xff0c;年营收超过5000亿元。该电器企业其整体业务分三大类&#xff1a;线上线下B2B2C业务、线下B2B业务以及DTC零售业务。 随着业务的发展&#xff0c;该电器品牌对2B业务及DTC业务的数字化系统能力支撑需要更加全面和立体&#xff0c;以适应业务…