多线程案例——阻塞队列
目录
一、阻塞队列
1. 生产者消费者模型
(1)解耦合
(2)“削峰填谷”
2. 标准库中的阻塞队列
3. 自己实现一个阻塞队列(代码)
4. 自己实现生产者消费者模型(代码)
一、阻塞队列
阻塞队列同样是一个符合先进先出的队列,相比于普通队列,阻塞队列还有其他的功能:
- 1. 线程安全
- 2. 产生阻塞效果
- (1)如果队列为空,尝试出队列,就会出现阻塞,阻塞到队列不为空为止;
- (2)如果队列满了,尝试入队列,就会出现阻塞,阻塞到队列不为满为止。
基于上述特点的阻塞队列,我们就可以实现“生产者消费者模型”。(处理多线程问题的一个典型方式)。
1. 生产者消费者模型
生产者消费者模型:通过一个容器(阻塞队列),来解决生产者消费者之间的强耦合问题。(生产者生产商品,消费者购买商品)
开发实际生活中,使用的 阻塞队列 并不是一个简单的数据结构,而是一组专门的服务器程序。里面也不仅仅包含了阻塞队列的功能,还有其他的功能。这样的队列叫做 “消息队列” 。
(1)解耦合
假设现在有两个服务器 A 和 B ,A 作为入口服务器(直接接收用户的网络请求),B 作为应用服务器(给 A 提供服务)。
假设不使用 生产者消费者模型:
即生产者和消费者之间直接进行沟通,则具有 强耦合性 。
强耦合性 体现在:
当开发 A 代码的时候就得充分了解 B 提供的一些接口,开发 B 代码的时候也得充分了解到 A 是怎么调用的。当我们想把 B 换成 C 时,A 的代码就得发生较大的改动,并且如果 B 崩溃了,也可能导致 A 也崩溃。
因此强耦合性是一种不好的现象。 而生产者消费者模型刚好可以降低这里的耦合,即模块和模块之间的联系尽可能的少。
当 A 给 B 发送请求时,A 先将请求写到阻塞队列中,然后 B 再从阻塞队列中取出请求。当 B 返回结果时,先把结果放到阻塞队列中,然后 A 再从阻塞队列中取出结果。
- 请求:A 是生产者,B 是消费者
- 响应:A 是消费者,B 是生产者
- 阻塞队列:作为交易场所
使用了生产者消费者模型后,A 只需要关注如何和队列进行交互,不需要认识 B ,B 也是如此。当 B 崩溃时,对 A 没有影响。A 崩溃时, B 也没有影响。相应的,如果将 B 换做 C ,A 也完全感知不到。 就达到了解耦合的目的。
(2)“削峰填谷”
使用生产者消费者模型,能够对于请求进行“削峰填谷”。(类似水库 -> 当雨季时,可以利用水库正常为稻田送水;当旱季时,打开水库,同样可以保证为稻田正常送水)
还是使用上述 A 和 B 服务器的例子。假设我们没有使用生产者消费者模型:
削峰:
当有大量请求时,A 服务器作为入口服务器,计算量轻,不会造成太大影响。而因为强耦合性,B 也会突然暴涨,B 作为应用服务器,计算量比 A 大得多,可能就直接造成崩溃了。
如果使用了生产者消费者模型,遇到请求暴涨的情况:
A 的请求暴涨,导致了阻塞队列的暴涨,因为阻塞队列只是存储数据,因此没有太大影响。B 这边依然按照原来的速度消费数据,不会因为 A 的暴涨而引起暴涨。因此 B 就被保护起来了,不会因为这种请求暴涨而崩溃,即 削峰 。(让 B 感受不到)
填谷:
当 A 的请求很少,而 B 依然会按照原来的速度消费数据。虽然 A 的请求少,但是阻塞队列中还有挤压的数据,就能够让 B 正常运行。
2. 标准库中的阻塞队列
Java标准库中自带了阻塞队列,当我们编写代码时需要使用阻塞队列时,直接使用即可。
- BlockingQueue 是一个接口,真正实现的类时 LinkedBlockingQueue。(类似List,LinkedList和ArrayList)
- put:阻塞式出队列
- take:阻塞式出队列
- (也有 offer、poll、peek等普通队列有的方法,但是这些方法不带有阻塞特性)
public class Demo1 {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingDeque<>();//阻塞式入队列,如果队列满,就会阻塞queue.put("jiu");//阻塞式出队列,如果队列空,就会阻塞System.out.println(queue.take()); //结果为 jiu}
}
3. 自己实现一个阻塞队列(代码)
要实现一个阻塞队列:一个普通队列 + 线程安全 + 阻塞功能 。
① 实现队列(先进先出)可以通过链表或者数组,这里使用数组。——> 循环队列(队首head 和 队尾 tail)
- 入队列:把新元素放到 tail 的位置,tail++
- 出队列:取出 head 位置的元素,head++
- 循环队列:当 head 或者 tail 到达数组末尾时,就需要从头开始(= 0),重新循环。
- 判空 / 满:要实现循环队列,这是一个重要问题。我们用 变量 size 记录元素的个数,进行判断。
② 实现线程安全。因为在方法中全是共享变量,所以可以直接对整个方法进行加锁,也可以对整个代码块进行加锁,同时专门设置一个锁对象locker。
③ 实现阻塞效果。利用 wait 和 notify 来实现。使用哪个对象加锁就要用哪个对象 wait
- 对于 put 来说,阻塞条件:队列为满。而 put 中的 wait 要由 take 来唤醒。(take 成功了队列就不满了)
- 对于 take 来说,阻塞条件:队列为空。而 take 中的 wait 要由 put 来唤醒。(put 成功了队列就不为空了)
如果有人等待,notify 可以唤醒;如果没人等待,notify 也没有任何影响。
//假设队列中存放的是整型元素
class MyBlockingQueue {private int[] array = new int[1000];private int size = 0;private int head = 0;private int tail = 0;private Object locker = new Object();//阻塞式入队列public void put(int value) throws InterruptedException {synchronized(locker) {if (size == array.length) {//说明队列满了,无法再入队列了//所以进入阻塞状态locker.wait();}//队列没有满,可以入队列//1.把值放到 tail 的位置array[tail] = value;tail++;//2.判断是否到队尾的情况(等价于 tail = tail % array.length)if (tail >= array.length) {tail = 0;}//3.size++size++;//入队列成功,队列非空,从阻塞队列中唤醒 takelocker.notify();}}//阻塞式出队列public int take() throws InterruptedException {synchronized(locker) {if (size == 0) {//队列为空,没有元素可以出//进入阻塞状态locker.wait();}//队列中有元素,可以出队列//1.取出 head 位置的值int ans = array[head];head++;//2. 判断是否到队尾if (head >= array.length) {head = 0;}//3. size--size--;//出队列成功,队列不为满,从阻塞队列中唤醒 putlocker.notify();return ans;}}
}
4. 自己实现生产者消费者模型(代码)
public class Demo2 {//自己实现生产者消费者模型public static MyBlockingQueue queue = new MyBlockingQueue();public static void main(String[] args) {Thread producer = new Thread(() -> {int num = 0;while(true) {try {queue.put(num);System.out.println("生产了: " + num);num++;//让生产者生产慢一点,消费者就得跟着生产者的步伐Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();Thread customer = new Thread(() -> {while(true) {try {int num = queue.take();System.out.println("消费了: " + num);} catch (InterruptedException e) {e.printStackTrace();}}});customer.start();}
}
相关文章:

多线程案例——阻塞队列
目录 一、阻塞队列 1. 生产者消费者模型 (1)解耦合 (2)“削峰填谷” 2. 标准库中的阻塞队列 3. 自己实现一个阻塞队列(代码) 4. 自己实现生产者消费者模型(代码) 一、阻塞队列…...

学习优秀博文(【国产MCU移植】手把手教你使用RT-Thread制作GD32系列BSP)有感 | 文末赠书5本
学习优秀博文(【guo产MCU移植】手把手教你使用RT-Thread制作GD32系列BSP)有感 一篇优秀的博文是什么样的?它有什么规律可循吗?优秀的guo产32位单片机处理器是否真的能成功替换掉stm32的垄断地位? 本文博主以亲身经历聊…...

写用例写的焦头烂额?看看摸鱼5年的老点工是怎么写的...
给你个需求,你要怎么转变成最终的用例? 直接把需求文档翻译一下就完事了。 老点工拿到需求后的标准操作: 第一步:解析需求 先解析需求-找出所有需求中的动词,再列出所有测试点。测试点过程不断发散,对于…...

基于深度学习的鸟类检测识别系统(含UI界面,Python代码)
摘要:鸟类识别是深度学习和机器视觉领域的一个热门应用,本文详细介绍基于YOLOv5的鸟类检测识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面。在界面中可以选择各种鸟类图片、视频以及开启摄像头进行检测识别…...

零基础搭建Tomcat集群(超详细)
💗推荐阅读文章💗 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》🌺MySQL系列🌺👉2️⃣《MySQL系列教程》🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》…...

机器学习自学笔记——聚类
聚类的基本概念 聚类,顾名思义,就是将一个数据集中各个样本点聚集成不同的“类”。每个类中的样本点都有某些相似的特征。比如图书馆中,会把成百上千的书分成不同的类别:科普书、漫画书、科幻书等等,方便人们查找。每…...

注意下C语言整形提升
C语言整形提升 C语言整形提升是指在表达式中使用多种类型的数据时,编译器会自动将较小的类型转换为较大的类型,以便进行运算。在C语言中,整型提升规则如下: 如果表达式中存在short类型,则将其自动转换为int类型。 如…...

Go panic的学习
一、前言 我们的应用程序常常会出现异常,包括由运行时检测到的异常或者应用开发者自己抛出的异常。 异常在一些其他语言中,如c、java,被叫做Exception,主要由抛出异常和捕获异常两部分组成。异常在go语言中,叫做pani…...

讲解Linux中samba理论讲解及Linux共享访问
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放࿰…...

【C++笔试强训】第三十二天
🎇C笔试强训 博客主页:一起去看日落吗分享博主的C刷题日常,大家一起学习博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话:夜色难免微凉,前方必有曙光 🌞。 💦&a…...

OpenAI GPT-4震撼发布:多模态大模型
OpenAI GPT-4震撼发布:多模态大模型发布要点GPT4的新功能GPT-4:我能玩梗图GPT4:理解图片GPT4:识别与解析图片内容怎样面对GPT4申请 GPT-4 API前言: 🏠个人主页:以山河作礼。 📝📝:本文章是帮助大家更加了…...

手把手教你 在linux上安装kafka
目录 1. 准备服务器 2. 选一台服务器配置kafka安装包 2.1 下载安装包 2.2 解压安装包 2.3 修改配置文件 3. 分发安装包到其他机器 4. 修改每台机器的broker.id 5. 配置环境变量 6. 启停kafka服务 6.1 启动kafak服务 6.2 停止kafka服务 1. 准备服务器 1.买几台云服务…...

Spring Cloud(微服务)学习篇(五)
Spring Cloud(微服务)学习篇(五) 1 nacos配置文件的读取 1.1 访问localhost:8848/index.html并输入账户密码后进入nacos界面并点击配置列表 1.2 点击右侧的号 1.3 点击加号后,进入新建配置界面,并做好如下配置 1.4 往下翻动,点击发布按钮 1.5 发布成功后的界面 1.6 在pom.xml…...

道阻且长,未来可期,从GPT-4窥得通用人工智能时代的冰山一角!
大家这两天是不是又被满屏的ChatGPT相关的文章信息给轰炸得不轻,说实话,我真的对ChatGPT的热度如此经久不衰这个问题非常感兴趣。从去年刚面世时,小范围内造成的行业震荡,到今年二月份铺天盖地得铺舆论造势,引发全民热…...

百度将?百度已!
仿佛一夜之间,创业公司OpenAI旗下的ChatGPT就火遍全球。这是一场十分罕见的科技盛宴。下到普通用户,上到各科技大厂都在讨论ChatGPT的前景,国外的微软、谷歌,国内的百度、腾讯、阿里等等都在布局相关业务。比尔盖茨更是称ChatGPT与…...

内核实验(三):编写简单Linux内核模块,使用Qemu加载ko做测试
文章目录一、篇头二、QEMU:挂载虚拟分区2.1 创建 sd.ext4.img 虚拟分区2.2 启动 Qemu2.3 手动挂载 sd.ext4.img三、实现一个简单的KO3.1 目录文件3.2 Makefile3.3 编译3.3.1 编译打印3.3.2 生成文件3.4 检查:objdump3.4.1 objdump -dS test\_1.ko3.4.2 o…...

女子举重问题
一、问题的描述 问题及要求 1、搜集各个级别世界女子举重比赛的实际数据。分别建立女子举重比赛总成绩的线性模型、幂函数模型、幂函数改进模型,并最终建立总冠军评选模型。 应用以上模型对最近举行的一届奥运会女子举重比赛总成绩进行排名,并对模型及…...

试题 历届真题 循环小数【第十一届】【决赛】【Python】
试题 历届真题 循环小数【第十一届】【决赛】【Python】 题目来源:第十一届蓝桥杯决赛 http://lx.lanqiao.cn/problem.page?gpidT2891 资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制ÿ…...

关于类型转换
隐式转换先看个例子int a {500}; unsigned b {1000}; std::cout<<a-b;这里的输出结果并不为-500。因为最后输出结果的类型自动转换成了unsigned,unsigned是正整数型类型转换顺序表(由高到低)long doubledoublefloatunsigned long long long longunsigned long…...

蓝桥杯冲击-02约数篇(必考)
文章目录 前言 一、约数是什么 二、三大模板 1、试除法求约数个数 2、求约数个数 3、求约数之和 三、真题演练 前言 约数和质数一样在蓝桥杯考试中是在数论中考察频率较高的一种,在省赛考察的时候往往就是模板题,难度大一点会结合其他知识点考察&#x…...

122.(leaflet篇)leaflet地图图片之间存在缝隙
听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 存在缝隙–效果如下所示: 解决缝隙–效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html>…...

4.类的基本概念
目录 4.1 类的概述 类是一种活动的数据结构 4.2 程序和类:一个快速实例 4.3 声明类 4.4 类成员 4.4.1 字段 1.显示和隐式字段初始化 2. 声明多个字段 4.4.2 方法 4.5 创建变量和类的实例 4.6 为数据分配内存 合并这两个步骤 4.7 实例成员 4.8 访问修饰…...

有图解有案例,我终于把 Condition 的原理讲透彻了
哈喽大家好,我是阿Q! 20张图图解ReentrantLock加锁解锁原理文章一发,便引发了大家激烈的讨论,更有小伙伴前来弹窗:平时加解锁都是直接使用Synchronized关键字来实现的,简单好用,为啥还要引用Re…...

Linux之找回root密码
文章目录前言一、启动系统二、进入编辑界面三、修改密码前言 当我们使用root用户登陆Linux时,忘记了登陆密码,改怎样修改登陆密码呢,接下来将介绍如何修改root密码 一、启动系统 首先,启动系统,进入开机界面&#x…...

stack_queue | priority_queue | 仿函数
文章目录1. stack 的使用2. stack的模拟实现3. queue的使用4. queue的模拟实现5. deque ——双端队列deque优缺点6. priority_queue ——优先级队列1. priority_queue的使用2. priority_queue的模拟实现push——插入pop ——删除top —— 堆顶仿函数问题完整代码实现1. stack 的…...

第十四届蓝桥杯三月真题刷题训练——第 14 天
目录 第 1 题:组队 题目描述 运行限制 代码: 第 2 题:不同子串 题目描述 运行限制 代码: 思路: 第 3 题:等差数列 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码: 思…...

【Hadoop-yarn-01】大白话讲讲资源调度器YARN,原来这么好理解
YARN作为Hadoop集群的御用调度器,在整个集群的资源管理上立下了汗马功劳。今天我们用大白话聊聊YARN存在意义。 有了机器就有了资源,有了资源就有了调度。举2个很鲜活的场景: 在单台机器上,你开了3个程序,分别是A、B…...

技术掉:PDF显示,使用pdf.js
PDF 显示 场景: 其实直接显示 pdf 可以用 iframe 标签,但产品觉得浏览器自带的 pdf 预览太丑了,而且无法去除那些操作栏。 解决方案:使用 pdf.js 进行显示 第一步:引入 pdf.js 去官网下载稳定版的 pdf.js 文件 然后…...

有关pytorch的一些总结
Tensor 含义 张量(Tensor):是一个多维数组,它是标量、向量、矩阵的高维拓展。 创建 非随机创建 1.用数组创建 将数组转化为tensor np.ones([a,b]) 全为1 #首先导入PyTorch import torch#数组创建 import numpy as np anp.arr…...

基础IO【Linux】
文章目录:文件相关知识C语言文件IOstdin & stdout & stderr系统文件 IOopenclosewriteread文件描述符文件描述符的分配规则重定向dup2系统调用FILEFILE中的文件描述符FILE中的缓冲区理解文件相关知识 文件 文件内容 文件属性(每一个已经存在的…...