当前位置: 首页 > news >正文

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

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

清风的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();}

 运行结果:


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

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

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

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

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

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

 

相关文章:

  • 【UGUI】Unity教程:实现物品的拖拽功能
  • SpringBoot+mysql+vue实现大学生健康档案管理系统前后端分离
  • MySQL数据库的备份与恢复
  • 腾讯云手动下发指令到设备-用于设备调试
  • 全栈冲刺 之 一天速成MySQL
  • 知乎禁止转载的回答怎么复制做笔记?
  • 恒驰服务 | 华为云云上运维服务offering
  • 近期知识点随笔
  • 【Java】使用IntelliJ IDEA搭建SSM(MyBatis-Plus)框架并连接MySQL数据库
  • 【Git】修改提交信息(单次、批量)
  • ChatGPT等模型:到2026年,将消耗尽高质量训练数据
  • SQL数据迁移实战:从产品层级信息到AB测试表
  • 时序预测 | Python实现TCN时间卷积神经网络价格预测
  • 数据爬取+数据可视化实战_哪里只得我共你(Dear Jane)_词云展示----网易云
  • 关于电脑提示vcruntime140_1.dll无法继续执行代码的解决办法
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • linux安装openssl、swoole等扩展的具体步骤
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Puppeteer:浏览器控制器
  • vue.js框架原理浅析
  • yii2中session跨域名的问题
  • 闭包,sync使用细节
  • 彻底搞懂浏览器Event-loop
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • #define用法
  • (1)SpringCloud 整合Python
  • (3)llvm ir转换过程
  • (C语言)fgets与fputs函数详解
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (顺序)容器的好伴侣 --- 容器适配器
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • .bat批处理(六):替换字符串中匹配的子串
  • .Net Core和.Net Standard直观理解
  • .NET 读取 JSON格式的数据
  • // an array of int
  • /bin、/sbin、/usr/bin、/usr/sbin
  • @ModelAttribute注解使用
  • [Angular] 笔记 20:NgContent
  • [BZOJ] 2006: [NOI2010]超级钢琴
  • [C# 基础知识系列]专题十六:Linq介绍
  • [C#] 如何调用Python脚本程序
  • [C++ 从入门到精通] 12.重载运算符、赋值运算符重载、析构函数
  • [Go WebSocket] 多房间的聊天室(五)用多个小锁代替大锁,提高效率
  • [HTML]Web前端开发技术18(HTML5、CSS3、JavaScript )HTML5 基础与CSS3 应用——喵喵画网页
  • [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞
  • [one_demo_9]判断数组是否递增
  • [Python进阶] 消息框、弹窗:pywin32
  • [Redis]——数据一致性,先操作数据库,还是先更新缓存?