带你手搓阻塞队列——自定义实现

🌈🌈🌈今天给大家分享的是——阻塞队列的自定义实现,通过自定义实现一个阻塞队列,可以帮助我们更清晰、更透彻的理解阻塞队列的底层原理。

清风的CSDN博客

🛩️🛩️🛩️希望我的文章能对你有所帮助,有不足的地方还请各位看官多多指教,大家一起学习交流!🛩️🛩️🛩️

✈️✈️✈️动动你们发财的小手,点点关注点点赞!在此谢过啦!哈哈哈!✈️✈️✈️

 

目录

一、阻塞队列的作用 

二、阻塞队列实现 

2.1 普通队列实现 

2.1.1 构造方法 

2.1.2 入队列

 2.1.3 出队列

2.2 阻塞队列实现

2.2.1 保证线程安全 

2.2.2 保证内存可见性 

 2.2.3 阻塞功能的实现

三、基于自定义阻塞队列,模拟生产者消费者模型


 

一、阻塞队列的作用 

 一个分布式系统中,会经常出现这样的情况:有的机器能承担的压力更大,有的能承担的压力更小:

如果按照生产者消费者模型,那就另当别论了。

假设此时通过队列来让A和B进行交互:

 

二、阻塞队列实现 

2.1 普通队列实现 

在实现阻塞队列之前,我们先把普通的队列(基于数组的循环队列)进行一个简单的实现,然后通过进一步的改进,把普通的队列改造成一个阻塞队列。

class MyBlockingQueue{private int[] items;private int head = 0;//队列头指针private int tail = 0;//队列尾指针private int size = 0;//队列当前元素个数public MyBlockingQueue(){}//入队列public void put(int elem){}//出队列public int take(){}}

2.1.1 构造方法 

 public MyBlockingQueue(){this.items = new int[100];}

2.1.2 入队列

   //入队列public void put(int elem){if (size >= items.length){return;}items[tail] = elem;if (tail >= items.length){//判断尾指针是否到达末尾tail = 0;}tail++;size++;}

 2.1.3 出队列

    //出队列public int take(){if(size == 0){return -1;}int elem = items[head];if (head >= items.length){head = 0;}head++;size--;return elem;}

2.2 阻塞队列实现

现在,我们就把上面的队列改造成阻塞队列。

2.2.1 保证线程安全 

在当前的代码下,如果是多线程的情况,调用put或者take,这两个方法中都涉及到了对变量的修改,这样就会出现线程安全问题。这就需要我们进行加锁。

    //入队列public void put(int elem){synchronized (this){if (size >= items.length){return;}items[tail] = elem;if (tail >= items.length){tail = 0;}tail++;size++;}}
    //出队列public int take(){synchronized (this){if(size == 0){return -1;}int elem = items[head];if (head >= items.length){head = 0;}head++;size--;return elem;}}

2.2.2 保证内存可见性 

光加锁就够吗?我们可以看到,多线程的情况下,不光是对变量进行修改,还有读操作等等,那就有可能出现一个线程在读,另外一个线程在修改,这个读的线程没有读到。所以,此处除了加锁之外,还需要考虑内存可见性问题。也就是说,当其他线程进行修改的时候,我们要保证当前线程可以读到这个修改,所以我们把变量加上volatile关键字。

    volatile private int head = 0;volatile private int tail = 0;volatile private int size = 0;

 2.2.3 阻塞功能的实现

解决了上述问题后,我们就需要考虑一下如何实现阻塞功能了。

实现阻塞有两方面:

  • 当队列满的时候,再进行put(入队),就会产生阻塞。阻塞到队列中元素出队后,就去唤醒当前因队列满而被阻塞的状态。
  • 当队列空的时候,再进行take(出队),就会产生阻塞。阻塞到队列中有元素入队时,去唤醒当前因队列空而被阻塞的状态。 
    //入队列public void put(int elem) throws InterruptedException {synchronized (this){while (size >= items.length){//队列满了//return;this.wait();}items[tail] = elem;if (tail >= items.length){tail = 0;}tail++;size++;//成功入队this.notify();//唤醒因队列空而被阻塞的状态}}//出队列public int take() throws InterruptedException {synchronized (this){while (size == 0){//队列空//return -1;this.wait();}int elem = items[head];if (head >= items.length){head = 0;}head++;size--;this.notify();//使用这个notify唤醒队列满的阻塞状态return elem;}}}

 好了,经过上面的改进,我们就已经实现了一个简单的阻塞队列,下面是改进后的完整代码:

class MyBlockingQueue{private int[] items;volatile private int head = 0;volatile private int tail = 0;volatile private int size = 0;public MyBlockingQueue(){this.items = new int[100];}//入队列public void put(int elem) throws InterruptedException {synchronized (this){while (size >= items.length){//队列满了//return;this.wait();}items[tail] = elem;if (tail >= items.length){tail = 0;}tail++;size++;//成功入队this.notify();//唤醒因队列空而被阻塞的状态}}//出队列public int take() throws InterruptedException {synchronized (this){while (size == 0){//队列空//return -1;this.wait();}int elem = items[head];if (head >= items.length){head = 0;}head++;size--;this.notify();//使用这个notify唤醒队列满的阻塞状态return elem;}}}

三、基于自定义阻塞队列,模拟生产者消费者模型

实现阻塞队列之后,我们利用阻塞队列简单模拟一下生产者消费者模型:

    public static void main(String[] args) {MyBlockingQueue queue = new MyBlockingQueue();//生产者线程Thread product = new Thread(()->{int count = 0;while (true){try {queue.put(count);System.out.println("生产元素:>"+count);count++;Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});//消费者线程Thread consummer = new Thread(()->{while (true){try {int elem = queue.take();System.out.println("消费元素:>"+elem);} catch (InterruptedException e) {e.printStackTrace();}}});product.start();consummer.start();}

 运行结果:


🌈🌈🌈好啦,今天的分享就到这里!

🛩️🛩️🛩️希望各位看官读完文章后,能够有所提升。

🎉🎉🎉创作不易,还希望各位大佬支持一下!

✈️✈️✈️点赞,你的认可是我创作的动力!

⭐⭐⭐收藏,你的青睐是我努力的方向!

✏️✏️✏️评论:你的意见是我进步的财富!

 

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

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

相关文章

Java全栈基础篇--集合

集合 集合:集合是java中提供的一种容器,可以用来存储多个数据。 特点: 长度不固定,还可以存储不同的数据(但是一般都用同一类型) 集合和数组既然都是容器,它们有啥区别呢? 数组的长…

EasyAmplicon (易扩增子)-扩增子高通量序列分析软件流程及脚本-详细使用方法——来自刘永鑫团队的秘籍

其他不重要,重要的是方向,资源,还要好用,建议大家先看看论文介绍: https://doi.org/10.1002/imt2.83EasyAmplicon: An easy‐to‐use, open‐source, reproducible, and community‐based pipeline for amplicon data…

软件测试-测试用例案例及思维导图展示

自动售货机的测试用例 一个杯子的测试用例 一支笔的测试用例 朋友圈点赞的测试用例 功能测试 1点赞后是否显示结果 2.点赞后是否可以取消; 3.点赞取消后是否可以重复点赞; 4.共同好友点赞后,是否有消息提醒; 5.非共同好友点赞后,是否有消息提醒; 6.点击…

[数据结构]深入浅出讲解二叉树-平衡二叉树-左右旋转

树是一种数据结构,单位为Node(节点)。不同于链表的直线排列,树呈现一种自上而下的分层排序规则。 树->数据结构: 单元为Node(节点)->当这样的节点多了 就可以关联出不同的形态 一个父节点有一个左子节点,有…

uniapp-从后台返回的一串地址信息上,提取省市区进行赋值

1.这是接口返回的地址信息 2.要实现的效果 3.实现代码&#xff1a; <view class"address">{{item.address}}</view>listFun() {let url this.$url.url.positionInfoCompany;let param {page: this.page,limit: this.limit,keyword: this.keyword,};thi…

Python 入门

目录 编译性语言 解释型语言 Python语言介绍 Python安装 配置环境变量 Pycharm安装 Pycharm基本使用 编译性语言 解释型语言 计算机是不能理解高级语言的&#xff0c;更不能直接执行高级语言&#xff0c;它只能直接理解机器语言&#xff0c;所以使用任何高级语言编写的程…

应用于智慧金融的AI边缘计算盒子+AI算法软硬一体化方案

传统金融营业厅存在运营管理模式落后、资源投放不平衡、从业人员培训效果不达预期、客户体验割裂等普遍现象&#xff1b; 部署英码数字金融解决方案&#xff0c;将助力企业从传统金融模式快速向数字金融模式转变&#xff0c;可针对每一个客户定制个性化“一对一”服务&#xff…

基于springboot+vue的点餐系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

【23真题】耗时一周还原,终于有真题啦!

今天分享的是23年上海电力850的信号与系统回忆版试题及解析。上电的真题资源极其稀少&#xff0c;去年我找了3份&#xff0c;全部为假&#xff01;好在今年有刚刚上岸的同学全力协助&#xff0c;为大家弄出一份回忆版真题&#xff0c;希望对大家有帮助&#xff01; 小马哥Tips…

HarmonyOs 4 (二) HelloWord

目录 一 开发工具下载安装1.1 下载安装包1.2 下载相关依赖 二 开发者注册与个人实名认证三 第一个程序2.1 创建第一个程序2.2 认识开发者界面2.3 目录结构认识2.3.1 父目录认识2.3.2 AppScope 目录2.3.3 entry目录2.3.3.1 ets 目录2.3.3.2 resources目录 2.3.4 认识配置文件2.3…

马蹄集第34周

1.战神的对称谜题 不知道为什么超时&#xff01; def main():s input()result 0for i in range(len(s)):l i - 1r i 1while l > 0 and r < len(s) and s[l] s[r]:result max(result, r - l 1)l - 1r 1l ir i 1while l > 0 and r < len(s) and s[l] s…

【LeetCode】 160. 相交链表

相交链表 题目题解 题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&am…

FileInputStream 与 FileOutputStream

IO流&#xff1a;存贮和解决数据的存储方案 以上都是抽象类&#xff0c;不能创建实例 FileOutputStream 操作本地的文件&#xff0c;把文件写入本地磁盘 步骤&#xff1a; 1.创建字节输出流对象 参数是字符串表示的路径或者是File对象都是可以的如果文件不存在会创建一个新的…

redis笔记 -- 基础数据结构

redis笔记 基础的数据结构&#xff1a;string、list、hash、set、zset 容器型数据结构&#xff08;list、hash、set、zset&#xff09;通用规则 如果容器不存在&#xff0c;就创建一个&#xff0c;再进行操作如果容器里没有数据了&#xff0c;就立即删除&#xff0c;回收内存…

持续集成交付CICD:GitLabCI 运行前后端项目

目录 一、理论 1.spring项目自动构建 2.阿里云云效 Maven 3.Maven安装 4.Go安装 5.NPM安装 二、实验 1.GitLabCI 运行Maven项目 2.GitLabCI 运行Go项目 3.GitLabCI 运行NPM项目 三、问题 1.前端脚手架如何初始化项目 2.NPM下载如何指定 3.Go项目下载源如何指定 …

c++面试题

1.static的使用 1&#xff09;修饰局部变量&#xff1a;在函数内部使用static修饰局部变量&#xff0c;会使它成为静态局部变量。静态局部变量只会被初始化一次&#xff0c;且只有在第一次调用该函数时才会被初始化&#xff0c;之后每次调用该函数时都会保留上一次的值.从原来…

Vue生命周期:组件的生命之旅

&#x1f343;引言 在Vue.js中&#xff0c;每个组件都有其独特的生命周期。这个生命周期指的是组件从创建到销毁的一系列过程。了解并正确使用Vue的生命周期钩子&#xff0c;对于优化组件性能、管理组件状态以及实现特定功能至关重要。本文将详细介绍Vue的生命周期及其各个阶段…

SpringBoot+Redis编写一个抢红包雨的案例。附源码。

案例演示 SpringBootRedis编写一个抢红包雨的案例。附源码 1、案例分析&#xff0c;整体方案介绍 预备上线一个红包雨活动。这个红包雨的思路是活动开始前25分钟&#xff0c;在后台创建活动。然后前端用户进入&#xff0c;到点后将设置的金额拆分成多个小红包&#xff0c;开启倒…

转向更卷的奶茶 库迪这是要在咖啡上认怂?

卖咖啡刚满一年&#xff0c;陆正耀就带着名为“茶猫”的奶茶项目再度登场。 根据多位网友的爆料&#xff0c;茶猫采取和库迪咖啡一样的联营商模式&#xff0c;且有低价补贴策略&#xff0c;主打低价茶饮市场&#xff0c;全场统一定价6.9元。 熟悉的加盟模式&#xff0c;熟悉的…

使用type实现接口继承效果

如何使用type实现接口继承的效果 接口继承的写法 // 接口继承 interface Person {name: stringage: numberhobby?: string say: (content: string) > void } interface Student extends Person {score: number }使用 type 模拟 interface的继承效果 // 使用type实现类似…