【JavaSE】【多线程】阻塞队列

目录

  • 一、阻塞队列
    • 1.1 生产者消费者模型
    • 1.2 Java提供的阻塞队列
    • 1.3 实现一个简单生产者消费者模型
  • 二、自己实现阻塞队列
    • 2.1 成员变量
    • 2.2 构造方法
    • 2.3 put方法
    • 2.4 take方法
    • 2.5 最终代码

一、阻塞队列

阻塞队列:是一种特殊的队列,也有先进先出的特性。它是一种线程安全的队列。
有以下两个特性:

  • 当队列的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素。
  • 当队列的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素。
    阻塞队列的一个重要应用场景就是:实现生产者消费模型。

1.1 生产者消费者模型

生产者消费者模型:是多线程编程中的一种典型的编码技巧。用来降低生产者与消费者之间的耦合度。生产者和消费者之间的交易场所就是一个阻塞队列。

这样的模型的优势有以下两个:

  1. 解耦合,降低代码耦合度:
    像如果是A B两个服务器,之间直接进行交互,如果对A或者B中的数据进行修改操作,大概率就会影响到另一个服务器。而使用阻塞队列作为交易平台,我们修改服务器的数据时,由于阻塞队列中的结构固定,两个服务器之间的耦合度就降低。
  2. 削峰削谷:
    在服务器中,波峰就是请求量高的时候,波谷就是请求量低的时候。
    如果是AB两个服务器之间进行交互,当上游服务器A经历波峰,将大量请求传给服务器B的时候,服务器就有可能挂掉。
    因为上游服务器,干的活简单,消耗的资源少;而下游服务器,干的活复杂,消耗的的资源就多。
    但是如果我们将阻塞队列作为交易平台,那么服务器B就可以依据自己的节奏从队列中拿请求。

但是这样的模型也会付出代价:

  1. 引入阻塞队列之后整体结构会更加复杂。比如本来是AB两个服务器之间的交互,但引入一个作为阻塞队列的服务器(这种称为消息队列),就需要部署这个服务器,还要与AB实现交互。
  2. 效率也会有影响。

1.2 Java提供的阻塞队列

提供了一个BlockingDeque的接口(需要导java.util.concurrent.BlockingQueue包):

主要使用下面3个实现了BlockingDeque接口的来实例化阻塞队列:

  1. 链表实现的,LinkedBlockingDeque(需要导java.util.concurrent.LinkedBlockingDeque包):
  2. 数组实现的,ArrayBlockingDeque需要导`java.util.concurrent.ArrayBlockingDeque包):
  3. 小根堆实现的,PriorityBlockingDeque需要导java.util.concurrent.PriorityBlockingDeque包):

在阻塞队列中我们虽然可以使用队列中常用的出队列入队列方法,但是那些方法不带阻塞效果。带阻塞效果的入队列方法是put,出队列方法是take,这两个方法都会抛出InterruptedException异常。

1.3 实现一个简单生产者消费者模型

实现一个简单的生产者消费者模型:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;public class Demo {public static void main(String[] args) {BlockingQueue<Integer> blockingQueue = new LinkedBlockingDeque<>(1000);Thread producer = new Thread(() -> {int i = 0;while(true) {try {blockingQueue.put(i++);System.out.println(i + "入队列成功");} catch (InterruptedException e) {e.printStackTrace();}}});Thread consumer = new Thread(() -> {while(true){try {int x = blockingQueue.take();System.out.println(x + "出队列成功");} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();}
}

二、自己实现阻塞队列

我们使用数组来实现一个循环队列。
不知道循环队列的实现的可以看下面这个链接:队列

2.1 成员变量

  • 使用capacity代表数组的最大长度;
  • 使用size表示数组中元素的个数;
  • head表示队头元素的下标;
  • tail表示队尾元素的下标。
	private int capacity = 0xffff;private String[] elem ;//存储数组private int size;//存储元素个数private int head;//队头private int tail;//队尾

2.2 构造方法

提供两个构造方法:

  • 使用默认最大值初始化数组;
  • 使用传的参初始化数组。
    public MyBlockingQueue(int capacity) {this.capacity = capacity;elem = new String[this.capacity];}public MyBlockingQueue() {elem = new String[this.capacity];}

2.3 put方法

由于put和take方法都涉及到修改判断等操作,为避免原子性问题带来线程安全问题对该这些操作都要加锁。

在put方法中我们需要在队列满的时候发生阻塞,使用wait来等待。而在Java官方文档给出了建议我们使用循环语句来使用wait。

因为wait是除了notify唤醒外,还有可能被interrupt方法唤醒抛出异常,如果只要if,不用while,抛出异常后就会继续执行下面的逻辑,带来bug。而使用循环就不会,抛出异常后,会再次判断循环条件。

最后在入队成功后发出一个通知notify来唤醒由于队列空而阻塞等待的线程。

    public void put(String s) throws InterruptedException {synchronized (this) {while(size == elem.length) {this.wait();}elem[tail] = s;tail = (tail+1) % elem.length;size++;this.notify();}}

2.4 take方法

当队列为空的时候,跟put一样使用wait来阻塞。
最后在出队成功后发出一个通知notify来唤醒由于队列满而阻塞等待的线程。

 public String take() throws InterruptedException {synchronized (this) {while(size == 0) {this.wait();}String ret = elem[head];head = (head+1) % capacity;size--;this.notify();return ret;}}

2.5 最终代码

最终我们自己实现的一个简单的阻塞队列就如下:

public class MyBlockingQueue {private int capacity = 0xffff;private String[] elem ;//存储数组private int size;//存储元素个数private int head;//队头private int tail;//队尾public MyBlockingQueue(int length) {elem = new String[length];}public MyBlockingQueue() {elem = new String[this.capacity];}public void put(String s) throws InterruptedException {synchronized (this) {while(size == elem.length) {this.wait();}elem[tail] = s;tail = (tail+1) % capacity;size++;this.notify();}}public String take() throws InterruptedException {synchronized (this) {while(size == 0) {this.wait();}String ret = elem[head];head = (head+1) % capacity;size--;this.notify();return ret;}}
}

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

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

相关文章

【pytest】pytest注解使用指南

前言&#xff1a;在 pytest 测试框架中&#xff0c;注解&#xff08;通常称为装饰器&#xff09;用于为测试函数、类或方法提供额外的信息或元数据。这些装饰器可以影响测试的执行方式、报告方式以及测试的组织结构。pytest 提供了多种内置的装饰器&#xff0c;以及通过插件扩展…

Spark RDD 的 combineByKey、cogroup 和 compute 算子的作用

在面试中如果被问到 Spark RDD 的 combineByKey、cogroup 和 compute 算子的作用&#xff0c;建议从核心作用、实现原理&#xff08;源码解析&#xff09; 和 实际应用场景三方面组织答案。 1. combineByKey 核心作用 combineByKey 是一个通用的聚合算子&#xff0c;用于对 K…

el-table合并单元格之后,再进行隔行换色的且覆盖表格行鼠标移入的背景色的实现

el-table 中有现成的隔行换色功能&#xff0c;只要增加 stripe 属性即可。但是如果有单元格合并的话&#xff0c;这个属性就不可用了。这时候我们就需要动点小心思了。 基于相同字段进行合并 单元格合并&#xff1a;基于表头中的某一列&#xff0c;具有相同值的个数相加进行合…

OpenAI模型whisper 音频转文本

最近有一个音频转文本的需求&#xff0c;了解到了OpenAI的whisper模型可以实现。 Whisper 是 OpenAI 提供的一个通用语音识别模型&#xff0c;支持多语言的音频转文本功能&#xff0c;并具有较高的准确性。它的主要用途包括自动语音识别 (ASR)、语言翻译&#xff08;将音频直接…

微信小程序样式

一、尺寸单位rpx rpx是小程序新增加的自适应单位&#xff0c;可以根据不同设备的屏幕宽度进行自适应缩放。 小程序规定任何型号手机屏幕宽度都是750rpx。 提示&#xff1a;微信小程序的设计稿宽度建议是750px&#xff0c;这样就节省单位换算了。 二、全局样式和局部样式 1、…

Vue Router两种路由实现方式异同点总结

Vue.js 中的路由主要通过 Vue Router 实现&#xff0c;它支持多种路由模式&#xff0c;其中最常用的两种是 hash 模式和 history 模式。这两种模式在 URL 结构、工作原理以及对服务器配置的要求上有所不同。 1.Hash 模式&#xff08;默认模式&#xff09; ‌URL 结构‌&#…

Guava Cache

Guava Cache 单应用、本地缓存依赖 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1-jre</version> </dependency>示例 import com.google.common.cache.*;import java.util.…

ubuntu下连接了192.168.1.x和192.168.2.x两个网络段,如何让这个两个网段互相通信?

在 Ubuntu 上连接两个网络段&#xff08;如 个人终端A 192.168.1.10 和 个人终端B 192.168.2.10&#xff09;&#xff0c;需要配置路由和网络转发功能&#xff0c;使这两个网段能够相互通信。以下是实现方法&#xff1a; 步骤 1&#xff1a;确认网络配置 1. 确保 Ubuntu 机器…

汽车资讯新高度:Spring Boot技术飞跃

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

Go LCL、Go CEF 与 Go WV:打造跨平台桌面应用的利器

大家好&#xff0c;我是YC&#xff0c;在这里给大家分享Go LCL、Go CEF 与 Go WV&#xff1a;打造跨平台桌面应用的利器 如对此系统感兴许或需要源码&#xff0c;欢迎私信or留言&#xff01; Go LCL&#xff1a;原生控件的跨平台之旅 开源地址: https://github.com/energye/…

03-axios常用的请求方法、axios错误处理

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

子集选择——基于R语言实现(最优子集选择法、逐步回归法、Lasso回归法、交叉验证法)

( a )使用 rnorm() 函数生成预测变量X(n=100)与噪声向量 ϵ \epsilon ϵ(n=100) set.seed(1) x<-rnorm(100)#预测变量X eps<-rnorm(100)#噪声向量$\epsilon$( b ) 生成响应变量Y(n=100),

机器学习基础04_朴素贝叶斯分类决策树分类

目录 一、朴素贝叶斯分类理论 1、贝叶斯分类理论 2、条件概率 3、全概率公式 4、贝叶斯推断 5、朴素贝叶斯推断 6、拉普拉斯平滑系数 二、决策树分类 1、相关概念 2、基于信息增益决策树的建立 3、基于基尼指数决策树的建立 一、朴素贝叶斯分类理论 1、贝叶斯分类理…

git日志查询和导出

背景 查看git的提交记录并下载 操作 1、找到你idea代码的路径&#xff0c;然后 git bash here打开窗口 2、下载所有的日志记录 git log > commit.log3、下载特定日期范围内记录 git log --since"2024-09-01" --until"2024-11-18" 你的分支 > c…

Qt 和 WPF(Windows Presentation Foundation)

Qt 和 WPF(Windows Presentation Foundation)都是用于构建桌面应用程序图形用户界面的框架,它们在很多方面有相似之处,但也存在一些关键的差异。下面是它们的主要相同点和区别点。 相同点: 用于构建桌面应用程序: Qt 和 WPF 都主要用于构建具有丰富用户界面(GUI)的桌面…

后台管理系统(开箱即用)

很久没有更新博客了&#xff0c;给大家带上一波福利吧,大佬勿扰 现在市面上流行的后台管理模板很多,若依,芋道等,可是这些框架对我们来说可能会有点重,所以我自己从0到1写了一个后台管理模板,你们使用时候可扩展性也会更高 项目主要功能: 成员管理&#xff0c;部门管理&#…

Spring Boot 牛刀小试 org.springframework.boot:spring-boot-maven-plugin:找不到类错误

今天看了下书翻了下Spring Boot的用法&#xff0c;下载idea后&#xff0c; 反复出现org.springframework.boot:spring-boot-maven-plugin:找不到类错误&#xff0c;后来看了下调试窗口&#xff0c;发现是连不上maven的网站443错误&#xff0c;解决思路很简单&#xff0c;把ide连…

数据中心的拥塞控制

前面提到&#xff0c;如今的高速网络方案集中在卸载主机网络能力&#xff0c;降低主机处理时延&#xff0c;甚至 PFC 用网络排队时延换主机时延&#xff0c;本末倒置。而在拥塞控制领域仍是传统的 AIMD 闭环反馈&#xff0c;ECN&#xff0c;QCN 那一套&#xff0c;但拥塞是端到…

【网络云计算】2024第48周-技能大赛-初赛篇

文章目录 1、比赛前提2、比赛题目2.1、 修改CentOS Stream系统的主机名称&#xff0c;写出至少3种方式&#xff0c;并截图带时间戳和姓名&#xff0c;精确到秒&#xff0c;否则零分2.2、 创建一个名为你的名字的拼音的缩写的新用户并设置密码&#xff0c;将用户名添加到 develo…

SAP+Internet主题HTML样式选择

SAP目前只支持三种HTML样式选择&#xff1a; 样式一 背景色&#xff1a;深色&#xff0c;蓝 特点&#xff1a;适中型排列&#xff0c;与SAP界面排列相同&#xff0c;富含UI特征&#xff0c;整齐美观 URL地址&#xff1a;http://cn1000-sap-01.sc.com:8000/sap/bc/gui/sap/it…