【多线程】阻塞队列实现原理代码实现

news2024/11/15 15:45:06

目录

一、概念

二、优势

三、原理(代码逐步实现)

四、BlockingQueue的使用


 

一、概念

阻塞队列是一种的特殊的队列,他是带有阻塞的线程安全的队列。当队列已满时入队操作就会进入阻塞,当队列不空时才能执行入队操作;当队列为空时出队操作就会进入阻塞,当有元素插入阻塞就会被唤醒执行。它经常用于实现生产者消费者模型。

二、优势

1、阻塞队列的引入,有利于代码的解耦合

2、削峰填谷

3、异步提速

与消息队列类似,可参看【RabbitMQ】初识消息中间件MQ_西瓜霜润喉片的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_61903414/article/details/130138361?spm=1001.2014.3001.5501

三、原理(代码逐步实现)

这里我们基于数组实现的循环队列来进行阻塞队列的实现,首先我们需要实现一个循环队列

【数据结构】队列与Queue接口_队列是接口吗_西瓜霜润喉片的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_61903414/article/details/128410608?spm=1001.2014.3001.5501

lass MyBlockingQueue{
	private int[] queue = new int[100];  // 用于实现队列的数组
	private int front;                   // 队首下标
	private int rear;                    // 队尾下标
	private int size;                    // 元素个数

	/**
	 * 入队
	 * @param data 待入队数据
	 */
	public void put(int data){
		if((rear + 1) % this.queue.length == front){
			// 队列满了,则不进行入队
			return;
		}
		
		// 将数据入队后队尾下标自增
		this.queue[rear] = data;
		rear = (rear + 1) % this.queue.length;
	}

	/**
	 * 出队
	 * @return 队首元素
	 */
	public Integer take(){
		if (rear == front){
			// 队列为空
			return null;
		}
		
		// 获取队首元素
		int val = this.queue[front];
		// 队首下标自增
		front = (front + 1) % this.queue.length;
		// 返回
		return val;
	}
}

 上述代码仅是简单的循环队列,在多线程的环境下是存在问题的,且还没有实现阻塞功能,上述入队出队方法都设计修改存在线程安全问题,此时我们可以通过加锁的方法来先保证线程安全

class MyBlockingQueue{
	private int[] queue = new int[100];  // 用于实现队列的数组
	private int front;                   // 队首下标
	private int rear;                    // 队尾下标
	private int size;                    // 元素个数

	/**
	 * 入队
	 * @param data 待入队数据
	 */
	public void put(int data){
		synchronized (this) {
			if ((rear + 1) % this.queue.length == front) {
				// 队列满了,则不进行入队
				return;
			}

			// 将数据入队后队尾下标自增
			this.queue[rear] = data;
			rear = (rear + 1) % this.queue.length;
		}
	}

	/**
	 * 出队
	 * @return 队首元素
	 */
	public Integer take(){
		synchronized (this) {
			if (rear == front) {
				// 队列为空
				return null;
			}

			// 获取队首元素
			int val = this.queue[front];
			// 队首下标自增
			front = (front + 1) % this.queue.length;
			// 返回
			return val;
		}
	}
}

在保证了线程安全后我们可以根据队满入队阻塞、队空出队阻塞来实现阻塞功能,在put方法中先进行if判断是否满了,如果满了我们进入if中使用wait方法进行阻塞,直到有线程调用take方法在take方法执行结束前调用notify唤醒阻塞;同理take方法进入时先if判断是否为空,如果为空则调用wait进入阻塞

class MyBlockingQueue{
	private int[] queue = new int[100];  // 用于实现队列的数组
	private int front;                   // 队首下标
	private int rear;                    // 队尾下标
	private int size;                    // 元素个数

	/**
	 * 入队
	 * @param data 待入队数据
	 */
	public void put(int data) throws InterruptedException {
		synchronized (this) {
			if ((rear + 1) % this.queue.length == front) {
				// 队列满了,则不进行入队
				this.wait();
			}

			// 将数据入队后队尾下标自增
			this.queue[rear] = data;
			rear = (rear + 1) % this.queue.length;
			// 唤醒阻塞的出队线程
			this.notify();
		}
	}

	/**
	 * 出队
	 * @return 队首元素
	 */
	public Integer take() throws InterruptedException {
		synchronized (this) {
			if (rear == front) {
				// 队列为空
				this.wait();
			}

			// 获取队首元素
			int val = this.queue[front];
			// 队首下标自增
			front = (front + 1) % this.queue.length;
			// 唤醒阻塞的入队线程
			this.notify();
			// 返回
			return val;
		}
	}
}

此时阻塞功能也实现了,但是还存在一些问题,如果队列为空,有一个线程1执行take操作进入了阻塞,若干时间后该线程被唤醒,继续往后执行,但是此时该线程唤醒可能不是因为入队操作而唤醒的,也可能是因为其他原因(如interrupt) ,所以线程被唤醒后队列不一定非空,put方法也同理,所以我们需要在线程被唤醒后再次进行判断是否非空或者非满,此时我们可以将if改为while即可

class MyBlockingQueue{
	private int[] queue = new int[100];  // 用于实现队列的数组
	private int front;                   // 队首下标
	private int rear;                    // 队尾下标
	private int size;                    // 元素个数

	/**
	 * 入队
	 * @param data 待入队数据
	 */
	public void put(int data) throws InterruptedException {
		synchronized (this) {
			if ((rear + 1) % this.queue.length == front) {
				// 队列满了,则不进行入队
				this.wait();
			}

			// 将数据入队后队尾下标自增
			this.queue[rear] = data;
			rear = (rear + 1) % this.queue.length;
			// 唤醒阻塞的出队线程
			this.notify();
		}
	}

	/**
	 * 出队
	 * @return 队首元素
	 */
	public Integer take() throws InterruptedException {
		synchronized (this) {
			if (rear == front) {
				// 队列为空
				this.wait();
			}

			// 获取队首元素
			int val = this.queue[front];
			// 队首下标自增
			front = (front + 1) % this.queue.length;
			// 唤醒阻塞的入队线程
			this.notify();
			// 返回
			return val;
		}
	}
}

四、BlockingQueue的使用

public static void main(String[] args){
    BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(100);
    queue.put(100);   // 带阻塞功能入队,下面是出队
    queue.take();
}

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

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

相关文章

抓安卓日志命令

临时生成logcat文件 ctrl z 是停止 adb logcat -> /home/log/log.txt 导出全部日志 adb root adb remount 只抓安卓日志 logcat &#xff08;所有保存的日志&#xff0c;中途关机和开机都有&#xff09; adb pull log/android D:\73log\1android 抓全部日志&#x…

《DevOps实践指南》- 读书笔记(四)

DevOps实践指南 Part 3 第一步 &#xff1a;流动的技术实践11. 应用和实践持续集成11.1 小批量开发与大批量合并11.2 应用基于主干的开发实践11.3 小结 12. 自动化和低风险发布12.1 自动化部署流程12.1.1 应用自动化的自助式部署12.1.2 在部署流水线中集成代码部署 12.2 将部署…

从零开始,手把手教你视频直播app源码开发

在今天移动互联网时代&#xff0c;视频直播成为了人们沟通、互动和分享的重要方式。如果你梦想着拥有自己的视频直播应用程序&#xff0c;那么现在是时候开始学习开发视频直播app的源码了&#xff01;本文将带您从零开始&#xff0c;手把手教你如何开发视频直播app源码&#xf…

数据通信网络之IPv6基础

文章及资源归档至公众号【AIShareLab】&#xff0c;回复 通信系统与网络 可获取。 文章目录 一、目的二、环境及网络拓扑三、需求四、步骤及结果分析 一、目的 掌握网络设备静态IPv6 地址配置的方法。掌握IPv6 地址无状态自动配置的应用。掌握通过DHCPv6 部署IPv6 地址配置自动…

inappropriate address 127.0.0.1 for the fudge command, line ignored 时间同步的时候报错

1、安装ntp服务后&#xff0c;启动ntpd正常&#xff0c;但是在查看ntpd服务状态时&#xff0c;有一个红色的报错&#xff0c;报错信息如下&#xff1a; inappropriate address 127.0.0.1 for the fudge command, line ignored 2、解决方法&#xff1a;编辑ntp配置文件&#xf…

群晖NAS:通过Docker 部署宝塔面板【注册表:cyberbolt/baota】

群晖NAS&#xff1a;通过 Docker 部署宝塔面板【注册表&#xff1a;pch18/baota】 由于 docker 源地址被墙&#xff0c;在面板里面查询不到注册表&#xff0c;使用 ssh 命令行拉取 1、打开 SSH&#xff0c;链接后打开命令行 这里不赘述&#xff0c;具体自行百度 2、下载 镜像…

51单片机的简易篮球计分器倒计时仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

51单片机的简易篮球计分器倒计时仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单&&下载链接 51单片机的简易篮球计分器倒计时仿真设计( proteus仿真程序原理图报告讲解视频…

代码随想录第33天 | ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯

509. 斐波那契数 //法一&#xff1a; /*** param {number} n* return {number}*/ var fib function(n) {let bpnew Array(n)bp[0]0bp[1]1for(let i2;i<n;i){bp[i]bp[i-1]bp[i-2]}return bp[n] };//法二&#xff0c;时间少&#xff0c;空间少&#xff0c;只需要维护两个数值…

宝宝餐椅上亚马逊要求的合规标准有哪些?

宝宝餐椅上架亚马逊需要做什么认证&#xff1f; 大家都知道儿童餐椅是宝宝饮食的重要伙伴。它们为宝宝提供了一个舒适的环境&#xff0c;让宝宝在吃饭的时候更愉快&#xff0c;更健康。然而&#xff0c;许多家长可能不知道&#xff0c;亚马逊美国站售卖的儿童餐椅需要进行一系…

2023国赛数学建模E题思路代码 - 黄河水沙监测数据分析

# 1 赛题 E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c; 以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位…

HGDB-修改分区表名称及键值

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;N/A 版本&#xff1a;4.5.7 文档用途 使用存储过程拼接SQL&#xff0c;修改分区名称、分区键值、并重新加入主表&#xff0c;适用于分区表较多场景。 详细信息 说明&#xff1a;本文档为测试过程&#xff1…

15 轮转数组

轮转数组 题解1 环状替换&#xff08;学习思想&#xff09;&#xff08;空间O(1)&#xff09;题解2 翻转数组&#xff08;有意思好理解&#xff09;&#xff08;空间O(1)&#xff09;题解3 空间O(N)秒答 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&a…

护眼台灯A级好还是AA级好?盘点五款AA级台灯

台灯如何选择&#xff0c;随着人们生活水平的提高及科技的不断进步&#xff0c;台灯的品质也得到了极大的提高&#xff0c;在生活中很多时候都需要使用台灯&#xff0c;但是市面上的台灯那么多&#xff0c;台灯如何选择 国aa级是对台灯的照度进行的一个很重要的划分&#xff0…

【2023集创赛】安谋科技杯全国一等奖分享:基于安路PH1A60的3D图形体感游戏机

本文为2023年第七届全国大学生集成电路创新创业大赛&#xff08;“集创赛”&#xff09;安谋科技杯全国一等奖作品分享&#xff0c;参加极术社区的【有奖征集】分享你的2023集创赛作品&#xff0c;秀出作品风采&#xff0c;分享2023集创赛作品扩大影响力&#xff0c;更有丰富电…

leetcode 215.数组中第k大的元素

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;数组中第k大的元素 思路&#xff1a; 使用堆数据结构&#xff0c;大堆的堆顶是堆内最大的元素&#xff0c;也就是把当前堆 pop k - 1 次&#xff0c;第 k 次 top 出来的元素就是第 k 大的数。 代码&#xff1a; class …

Python中的Pathlib

迷途小书童 读完需要 4分钟 速读仅需 2 分钟 大家好&#xff0c;我是迷途小书童&#xff01; 今天给大家介绍 Python 中的内置库 Pathlib。 pathlib 模块是 Python 3.4 中新增的标准库&#xff0c;它提供了面向对象的文件系统路径处理方法。pathlib 致力于解决直接操作文件路径…

宝塔Linux面板的安装配置

一、 宝塔Linux面板的功能 宝塔面板是一款服务器管理软件&#xff0c;可以帮助用户建立网站&#xff0c;一键配置服务器环境&#xff0c;使得用户通过web界面就可以轻松的管理安装所用的服务器软件。 二、宝塔Linux面板的安装 宝塔官网地址&#xff1a;https://www.bt.cn/new…

【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; list模拟实现 1. 前言2. list类的大致框架与结构…

开源协议对比:局限性、应注意事项与详细对比

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

浙江工业大学MBA和浙江工商大学MBA哪个容易上岸?

在浙江省内&#xff0c;一般嫌弃浙大MBA项目学费贵的考生基本会从其它八个MBA项目中做衡量选择&#xff0c;其中浙工大MBA和浙工商MBA项目就是不少考生经常会做对比的项目&#xff0c;究竟哪个项目更容易上岸也是大家所关注的话题之一&#xff0c;立足浙江的杭州达立易考教育结…