[阻塞队列]

目录

1. 阻塞队列

2. 阻塞队列的优点

(1) 实现服务器之间的"低耦合".

(2) 实现"削峰填谷"的功能.

3. 阻塞队列代码举例

4. 自己实现阻塞队列


1. 阻塞队列

我们知道, 标准库中原有的队列Queue及其子类, 都是线程不安全的, 所以java封装了一个名为"阻塞队列" (Blocking Queue) 的接口, "阻塞队列"在普通队列的基础上做了扩充, 它有如下特性:

(1) "阻塞队列"是线程安全的.

(2) 如果队列为空时, 进行出队操作, 此时就会出现阻塞, 一直阻塞到其他线程往队列里增加元素为止 (一直阻塞到队列不为空为止).

(3) 如果队列为满时, 进行入队操作, 此时就会出现阻塞, 一直阻塞到其他线程取走队列元素为止 (一直阻塞到队列不为满为止).

[注]: 阻塞队列主要应用于"生产者-消费者"模型.

2. 阻塞队列的优点

(1) 实现服务器之间的"低耦合".

如果说A, B两台服务器之间是直接调用的关系, 那么编写A的代码时, 就会出现很多B的代码, 编写B的代码时, 也会出现很多A的代码, 这样的话两台服务器之间的耦合程度就非常高. 如果一台服务器挂掉的话, 另一台服务器也会受到很大的影响.

如果我们引入阻塞队列 ( 在服务器领域, 我们给这样的阻塞队列起了一个新的名字: "消息队列"  (Message Queue  "MQ" ) ), 让A与阻塞队列建立直接的联系(A只与阻塞队列通信), B与阻塞队列建立直接的联系(B也只与阻塞队列通信). 那么此时A不知道B的存在, 不也不会知道A的存在, A, B之间的耦合程度就非常低了, 现在即使B挂掉了, 对A也几乎没有影响.

(2) 实现"削峰填谷"的功能.

如果某一时刻, 客户端的请求量突然激增, 那么如果没有消息队列, 下游的服务器 ("下游服务器指的是要对数据或请求作进一步处理的服务器, 它比上游服务器要做的操作更复杂, 消耗的资源更多")很可能被巨大的请求量冲垮, 导致系统崩溃. 但是如果有了这样一个消息队列, 那么在请求量激增的时候, 消息队列就会起到一个"缓冲"的作用, 把巨大的请求量"挡住", 仍然以原有的速度 (或者比原本速度快一点的速度) 把请求传给下游服务器, 这样下游服务器就不会被冲垮. 而到了请求量偏低的时候, 消息队列又会把积攒的请求传给下游服务器, 让它不空闲下来.  这样一个"削峰填谷"的作用类似于"三峡大坝". 在汛期蓄水, 控制流速; 在旱期防水, 不至于让水流干涸.

注意: 消息队列也有其缺点, 比如: (1) 需要更多的机器来部署这样的消息队列. (2) 由于增加了消息队列这样一个东西, A与B之间通信的延时会变长.

3. 阻塞队列代码举例

我们可以看到, BlockingQueue这个接口有很多实现类, 这里我们以ArrayBlockingQueue为例举例. 

public class Demo26 {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);// 创建一个阻塞队列对象, 并指定其最大容量为3.queue.put("111");System.out.println("put成功");queue.put("111");System.out.println("put成功");queue.put("111");System.out.println("put成功");queue.put("111");System.out.println("put成功");}
}

运行上面这个程序, 我们可以看到, 前三个"111"都put成功, 但是执行到第四个put的时候, 发现队列已经满了, 那么这时候该线程就会进入阻塞状态. 直到其他线程取走队列元素(队列不满时), 才继续再进行put操作.

public class Demo27 {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);// 创建一个阻塞队列对象, 并指定其最大容量为3.queue.put("111");System.out.println("put成功");queue.put("111");System.out.println("put成功");queue.take();System.out.println("take成功");queue.take();System.out.println("take成功");queue.take();System.out.println("take成功");}
}

运行上面这个程序, 我们可以看到: 两个put成功, 两个take成功. 当执行到第三个take的时候, 发现此时队列为空, 那么此时线程就会进入阻塞状态, 直到其他线程往队列里新增元素(队列不空时), 才继续再进行take操作.

public class Demo28 {public static void main(String[] args) {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1000);// 生产者线程Thread t1 = new Thread(() -> {int i = 1;while (true) {try {queue.put(i);System.out.println("生产元素 " + i);i++;// 给生产操作, 加上 sleep, 生产慢点, 消费快点Thread.sleep(1300);} catch (InterruptedException e) {throw new RuntimeException(e);}}});// 消费者线程Thread t2 = new Thread(() -> {while (true) {try {Integer i = queue.take();System.out.println("消费元素 " + i);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

如上述代码, 我们给生产者线程加了sleep操作. 让生产得慢一点, 那么队列中没有元素的时候, 消费者线程只能阻塞等待, 等到队列中新增元素元素的时候再take, 进行消费元素.  所以, 上述代码的执行效果如下:

4. 自己实现阻塞队列

class MyBlockingQueue {private String[] data = null;private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;// private Object locker = new Object();public MyBlockingQueue(int capacity) {data = new String[capacity];}public void put(String s) throws InterruptedException {// 加锁的对象, 可以单独定义一个, 也可以直接就地使用 this.synchronized (this) {if (size == data.length) {// 队列满了// return;this.wait();}data[tail] = s;tail++;if (tail >= data.length) {tail = 0;}size++;this.notify();}}public String take() throws InterruptedException{String ret = "";synchronized (this) {if (size == 0) {// 队列为空// return null;this.wait();}ret = data[head];head++;if (head >= data.length) {head = 0;}size--;this.notify();}return ret;}
}

这里对变量加上volatile, 对操作加上synchronized, 保证线程安全. put方法中,当队列满的时候, 进入wait()等待,  take方法执行完take操作后会进行通知 notify() ;   take方法中,当队列空的时候, 进入wait()等待,  put方法执行完put操作后会进行通知 notify(); 相互通知, 保证了"阻塞"这个功能.

上述代码写成这个样子就比较完整了, 但是还存在一个小问题: 我们这里的wait()只有notify一种方式能唤醒吗? --> 显然不是, 那么, 比如说: 在 if (size == 0) 这里, 我们判定size == 0, 进入循环, 然后执行wait(), 进入等待状态. 如果此时wait()被提前唤醒了(eg: 被Interrupt唤醒), 而此时size还是0 (队列还没来得及新插入数据, wait()就被提前唤醒了), 那么唤醒之后继续往下执行程序必然会出现错误.  所以, 我们在这里可以做一个小小的改进: 将 if 替换成 wile . 这样做的目的就是为了能够"循环判定", 即使wait()被提前唤醒, 这里还会执行一次while的判定, 看 size 是否为0, 如果仍然是0, 那就继续wait(); 如果不为0了, 就执行后面的代码. 这样一来, 这个代码是不是就更加完善了呢? 不仅在这里, 日常开发中, 也有很多地方, 用 while 会比 if 好很多, 因为 while 是"循环判定", 而 if 只判定一次.

class MyBlockingQueue {private String[] data = null;private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;// private Object locker = new Object();public MyBlockingQueue(int capacity) {data = new String[capacity];}public void put(String s) throws InterruptedException {// 加锁的对象, 可以单独定义一个, 也可以直接就地使用 this.synchronized (this) {while (size == data.length) { //将if换成while// 队列满了// return;this.wait();}data[tail] = s;tail++;if (tail >= data.length) {tail = 0;}size++;this.notify();}}public String take() throws InterruptedException{String ret = "";synchronized (this) {while (size == 0) { //将if换成while// 队列为空// return null;this.wait();}ret = data[head];head++;if (head >= data.length) {head = 0;}size--;this.notify();}return ret;}
}

好了, 本篇文章就介绍到这里啦, 大家如果有疑问欢迎评论, 如果喜欢小编的文章, 记得点赞收藏~~

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

DCA-X 采样示波器

DCA-X 采样示波器 苏州新利通 | 综述 | DCA-X 宽带采样示波器属于我们的数字通信分析仪&#xff08;DCA&#xff09;系列。 这些示波器都是模块化平台&#xff0c;可对 50 Mb/s 到 224 Gb/s 的高速数字设计执行精准的测量。 您可以选择各种插入式模块来配置 DCA-X 主机&…

将webserver部署到公网(使用阿里云服务器)

阿里云轻量应用服务器介绍 这里我是用的是阿里云进行部署&#xff0c;阿里云推出的相关产品包括 云服务器 ECS 和轻量应用服务器。阿里云的指引和说明我觉得还是比较清楚详细的&#xff0c;适合新手。 先来介绍相关的一些名词&#xff1a; 云服务器 ECS&#xff08;Elastic …

【JavaEE进阶】Spring 事务和事务传播机制

目录 1.事务回顾 1.1 什么是事务 1.2 为什么需要事务 1.3 事务的操作 2. Spring 中事务的实现 2.1 Spring 编程式事务(了解) 2.2 Spring声明式事务 Transactional 对比事务提交和回滚的日志 3. Transactional详解 3.1 rollbackFor 3.2 Transactional 注解什么时候会…

Python 实现阿里滑块全攻略

阿里划块技术为开发者提供了高精度的视觉分割能力&#xff0c;而 Python 作为一种简洁高效的编程语言&#xff0c;可以轻松调用阿里划块接口&#xff0c;实现各种场景下的图像分割需求。 Python 调用阿里云分割抠图 - 商品分割接口的步骤如下&#xff1a;首先&#xff0c;开通…

[ComfyUI]Flux:繁荣生态魔盒已开启,6款LORA已来,更有MJ6写实动漫风景艺术迪士尼全套

今天&#xff0c;我们将向您介绍一款非常实用的工具——[ComfyUI]Flux。这是一款基于Stable Diffusion的AI绘画工具&#xff0c;旨在为您提供一键式生成图像的便捷体验。无论您是AI绘画的新手还是专业人士&#xff0c;这个工具都能为您带来极大的便利。 在这个教程中&#xff…

阿里云CDN稳定吗?

在互联网服务中&#xff0c;CDN&#xff08;内容分发网络&#xff09;扮演着至关重要的角色&#xff0c;它能够加速网站加载速度&#xff0c;提升用户体验。那么&#xff0c;作为市场上的领先者之一&#xff0c;阿里云的CDN到底稳定吗&#xff1f;九河云来和你说一说吧。 一、…

Matlab实现鹈鹕优化算法(POA)求解路径规划问题

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1内容介绍 鹈鹕优化算法&#xff08;POA&#xff09;是一种受自然界鹈鹕捕食行为启发的优化算法。该算法通过模拟鹈鹕群体在寻找食物时的协作行为&#xff0c;如群飞、潜水和捕鱼等&#xff0c;来探索问题的最优解。POA因其…

C++builder中的人工智能(22):在C+++中读取WAV格式的音频文件

在这篇文章中&#xff0c;我们将探讨如何在C中读取WAV格式的音频文件。音频文件是计算机科学和编程中的一个重要组成部分&#xff0c;正确使用音频可以为娱乐应用程序增添乐趣&#xff0c;或者在业务应用程序中提醒用户重要事件或状态变化。在这篇文章中&#xff0c;我们将解释…

.NET Core 应用程序如何在 Linux 中创建 Systemd 服务 ?

.NET Core 和 Linux 已经成为一个强大的组合&#xff0c;为开发人员提供了一个灵活、高性能的平台来构建和运行应用程序。在 Linux 上部署 .NET Core 应用程序的一个关键方面是利用 systemd 服务来确保应用程序顺利运行&#xff0c;在开机时自动启动&#xff0c;并在失败后重新…

@RestController 源码解读:解决 Web 开发中 REST 服务的疑难杂症

目录 一、RestContrller注解 1.1 查看底层源码 1.2 AliasFor注解说明 1.2.1 注解别名 1.2.2 元数据别名 1.3 value() 方法的作用 一、RestContrller注解 1.1 查看底层源码 首先编写如下内容&#xff1a; RestController public class TestController {} 按住 Ctrl &am…

vs2019托管调试助手 “ContextSwitchDeadlock“错误

错误描述 托管调试助手 "ContextSwitchDeadlock":“CLR 无法从 COM 上下文 0xd183e0 转换为 COM 上下文 0xd18328&#xff0c;这种状态已持续 60 秒。拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows 消息的情况下处理一个运行时间非常长…

H.264/H.265播放器EasyPlayer.js RTSP播放器关于webcodecs硬解码H265的问题

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方式&#xff0c…

免费在线图片翻译工具:PicTech

文章目录 简介编辑功能 简介 PicTech是一款免费的在线图片翻译工具。图片翻译&#xff0c;顾名思义就是把图片中的文字翻译成另外一种语言&#xff0c;并以图片的形式输出。这种功能在手机的词典软件中似乎还挺常见的&#xff0c;但作为一种在线工具我还是第一次见。 其使用过…

【Vue】Vue3.0(二十)Vue 3.0 中mitt的使用示例

上篇文章 【Vue】Vue3.0&#xff08;十九&#xff09;Vue 3.0 中一种组件间通信方式-自定义事件 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月11日12点23分 文章目录 一、mitt 在…

搭建监控系统Prometheus + Grafana

公司有个技术分享会&#xff0c;但是业务忙&#xff0c;没时间精心准备&#xff0c;所以就匆匆忙忙准备分享一下搭建&#xff08;捂脸哭&#xff09;。技术含量确实不多&#xff0c;但是分享的知识确实没问题。 以下是搭建过程&#xff1a; 一、讲解 Prometheus Prometheus 最…

蓝桥杯真题——班级活动

目录 题目链接&#xff1a;1.班级活动 - 蓝桥云课 题目描述 输入格式 输出格式 样例输入 样例输出 样例说明 评测用例规模与约定 解法一&#xff1a;Map集合处理 举个例子 Java写法&#xff1a; C写法&#xff1a; 运行时间 时间复杂度和空间复杂度 时间复杂度…

Win10下使用Anaconda安装GPU版本PyTorch

一、判断是否有Nvidia(英伟达)显卡 右键开始菜单&#xff0c;在弹出选项中选择任务管理器。 点性能选项&#xff0c;然后点GPU。在右上方会显示GPU名称&#xff0c;只有带NVIDIA的英伟达显卡的电脑才能安装GPU版本&#xff0c;否则其他的就只能安装CPU版本。 二、安装CUDA 首…

精品案例PPT | 企业架构及典型设计方案

本文全面介绍企业架构的理论和实践&#xff0c;包括企业架构的概述、元模型、视图、业务架构、应用架构、数据架构、技术架构以及企业架构管控等内容&#xff0c;有助于企业管理者理解和设计企业级的IT架构&#xff0c;确保架构的全局性、整体性、关联性、可控制性、可实现性和…

java--泛型

欢迎来到我的博客~~欢迎大家对我的博客进行指导~点击进入我的博客主页 目录 一、什么是泛型二、包装类2.1基本数据类型和对应的包装类2.2装箱和拆箱2.3 自动装箱和自动拆箱 三、引出泛型四、泛型类的使用4.1 语法4.2示例 五、泛型如何编译的六、泛型的上界6.1语法6.2 示例 七、…

【CentOS】中的Firewalld:全面介绍与实战应用(下)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Linux &#xff1a;从菜鸟到飞鸟的逆袭》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、iptables 时代 2、firewalld 时代 二、服务管…