【Java】实现阻塞队列-生产者/消费者模型

 上文中我们讲了Java库中自带的阻塞队列,并且讲了如何用阻塞队列来实现生产者消费者模型

【Java】用Java库中自带的阻塞队列以及用阻塞队列实现生产者-消费者模型

下面我们来讲如何用代码实现一个阻塞队列 

1、实现一个阻塞队列

阻塞队列 = 普通队列 + 线程安全 + 阻塞

(1)首先实现一个普通队列

class MyBlockingQueue{private int head = 0;private int tail = 0;private int size = 0;String[] array;public MyBlockingQueue(){array = new String[1000];}//取出队首元素public String take() throws InterruptedException {//如果队列为空,则返回nullif (size == 0){return null;}//取出队首元素String elem = array[head];//如果head已经到了队尾,那么下一个置0if(head == array.length){head = 0;}head++;size--;return elem;}//放入元素public void put(String elem) throws InterruptedException { if (size == array.length){return;}array[tail] = elem;if (tail == array.length){tail = 0;}tail++;size++;}
}

(2)线程安全 

由于put()和take()方法中对各个变量都进行了多次修改,因此我们在实现线程安全时,直接对这两段代码加锁

public String take() throws InterruptedException {synchronized{if (size == 0){return null;}String elem = array[head];if(head == array.length){head = 0;}head++;size--;return elem;}}
 public void put(String elem) throws InterruptedException { synchronized{if (size == array.length){return;}array[tail] = elem;if (tail == array.length){tail = 0;}tail++;size++;}}

并且为了防止内存可见性问题和指令重排序问题,我们给三个变量加上volatile关键字进行修饰

(什么是可见性问题和指令重排序问题?)

【Java】volatile-内存可见性问题

【Java】多线程-单例模式/volatile-指令重排序 

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

(3)阻塞

 最后再加上阻塞

取队首元素时,如果队列为空,那么我们直接进行阻塞;等到下一次在另一个线程放入元素时将其唤醒

放元素时,如果队列满了,我们将这个线程阻塞;等到队列可用时,我们在另一个线程唤醒

    public String take() throws InterruptedException {synchronized (this){if (size == 0){this.wait();}String elem = array[head];if(head == array.length){head = 0;}head++;size--;this.notify();return elem;}}public void put(String elem) throws InterruptedException {synchronized (this){if (size == array.length){this.wait();}array[tail] = elem;if (tail == array.length){tail = 0;}tail++;size++;this.notify();}}

注意他们唤醒的对应关系

(4)while循环

这其中还存在一个问题,那就是wait()的对象只能被notify()唤醒吗?

答案是不。除了用notify()唤醒,发生InterruptedException异常也可以将对象唤醒

假设队列为空的情况下,发生了InterruptedException异常,对象被唤醒,代码继续往下执行,再想取元素便会出错。因此这种情况下我们还要继续判断队列是否为空

为了解决这个问题,我们将if判断改为while()循环判断,就可以避免上面情况发生

//取出队首元素public String take() throws InterruptedException {synchronized (this){while (size == 0){this.wait();}String elem = array[head];if(head == array.length){head = 0;}head++;size--;this.notify();return elem;}}//放入元素public void put(String elem) throws InterruptedException {synchronized (this){//判断队列是否满了,如果满了则阻塞while (size == array.length){this.wait();}array[tail] = elem;if (tail == array.length){tail = 0;}tail++;size++;this.notify();}}

(5)完整代码

实现阻塞队列的完整代码如下

class MyBlockingQueue{private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;String[] array;public MyBlockingQueue(){array = new String[1000];}//取出队首元素public String take() throws InterruptedException {synchronized (this){while (size == 0){this.wait();}String elem = array[head];if(head == array.length){head = 0;}head++;size--;this.notify();return elem;}}//放入元素public void put(String elem) throws InterruptedException {synchronized (this){//判断队列是否满了,如果满了则阻塞while (size == array.length){this.wait();}array[tail] = elem;if (tail == array.length){tail = 0;}tail++;size++;this.notify();}}
}

2、实现生产者-消费者模型

 代码如下

class MyBlockingQueue{private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;String[] array;public MyBlockingQueue(){array = new String[1000];}//取出队首元素public String take() throws InterruptedException {synchronized (this){while (size == 0){this.wait();}String elem = array[head];if(head == array.length){head = 0;}head++;size--;this.notify();return elem;}}//放入元素public void put(String elem) throws InterruptedException {synchronized (this){//判断队列是否满了,如果满了则阻塞while (size == array.length){this.wait();}array[tail] = elem;if (tail == array.length){tail = 0;}tail++;size++;this.notify();}}
}public class demo2 {public static void main(String[] args) {MyBlockingQueue myBlockingQueue = new MyBlockingQueue();//生产者Thread thread1 = new Thread(()->{int n = 0;while (true){try {myBlockingQueue.put(n +"");} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("生产元素"+n);n++;try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//消费者Thread thread2 = new Thread(()->{while (true){try {System.out.println("消费元素" + myBlockingQueue.take());} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread1.start();thread2.start();}
}

运行结果如图

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

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

相关文章

机器学习实战第1天:鸢尾花分类任务

专栏介绍 欢迎订阅专栏——机器学习实战 机器学习实战_Nowl的博客-CSDN博客 纸上得来终觉浅 本专栏项目将着重于解决各类实际机器学习问题,带你上手各种场景的实际问题 数据集可以在我的资源中找到,也可以自行搜索 文中导入数据集的路径要改成自己的…

C++学习笔记——C++ deque和vector的区别

C中的std::deque(双端队列)和std::vector(向量)是两种不同的容器类型,它们有以下区别: 内部实现方式不同:std::deque使用了一种双端队列的数据结构,它由多个块(chunks&am…

【操作系统】文件系统的实现

文章目录 文件系统的层次结构文件系统的实现目录实现线性列表哈希表 文件的实现连续分配链接分配索引分配 文件存储空间管理空闲表法与空闲链表法成组链接法位示图法 文件系统的层次结构 文件系统从上往下分为了五层,分别是用户调用接口、文件目录系统、存取控制模…

SWT/Jface(1): 表格的创建和渲染

前言 使用JFace创建表格还是比较方便的, 如果仅仅是创建空表格的话, 以下2步即可完成: 创建TableViewer对象, 指定样式, 比如是否支持多行选择, 有无边框, 是否支持滚动条等创建TableColumn对象: 包括列展示名称, 宽度和样式等, 最终绑定到table对象 实例 创建表格 //注意…

设计模式-学习总结

学习总结 本文仅供自我学习使用 我是一个小白设计模式一.创建型模式1.单例模式(1).饿汉式(2).懒汉式,双检锁(3).静态内部类(4).枚举 2.原型模式3.工厂模式(1).简单工厂模式 4.抽象工厂模式5.建造者模式 二.结构型模式6.适配器模式7.组合模式8.装饰器模式9.外观模式1…

【AI】行业消息精选和分析(11月22日)

今日动态 👓 Video-LLaVA:视觉语言模型革新: - 图像和视频信息转换为文字格式。 - 多模态理解能力,适用于自动问答系统等。 📈 百度文心一言用户数达7000万: 🔊 RealtimeTTS:实时文本…

SpringBoot : ch06 整合 web (一)

前言 SpringBoot作为一款优秀的框架,不仅提供了快速开发的能力,同时也提供了丰富的文档和示例,让开发者更加容易上手。在本博客中,我们将介绍如何使用SpringBoot来整合Web应用程序的相关技术,并通过实例代码来演示如何…

《微信小程序案例大全》大学生期末大作业可以直接使用!!

前言 在大学生活中,期末大作业是锻炼和展示自己所学知识的重要时刻。微信小程序作为一种快速、便捷的应用开发方式,成为了大学生开发实践的热门选择。本文将为大家推荐一系列可以直接使用的微信小程序案例,包括仿真社交、图书管理、学习工具…

接口自动化测试实战经验分享,测试用例也能自动生成

作为测试,你可能会对以下场景感到似曾相识:开发改好的 BUG 反复横跳;版本兼容逻辑多,修复一个 BUG 触发了更多 BUG;上线时系统监控毫无异常,过段时间用户投诉某个页面无数据;改动祖传代码时如履…

CentOS 7 使用pugixml 库

安装 pugixml Git下载地址:https://github.com/zeux/pugixml 步骤1:首先,你需要下载pugixml 的源代码。你可以从Github或者源代码官方网站下载。并上传至/usr/local/source_code/ 步骤2:下载完成后,需要将源代码解压…

利用QRCode.js生成动态二维码页面

文章目录 QRCode.js简介HTML结构JavaScript生成动态二维码拓展功能1. 联系信息二维码2. Wi-Fi网络信息二维码 总结 🎉利用QRCode.js生成动态二维码页面 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客🎈该系列文章专栏…

微信小程序开发者工具] ? Enable IDE Service (y/N) ESC[27DESC[27C

在HBuilder运行微信小程序开发者工具报错 如何解决 打开微信小程序开发者工具打开设置--->安全设置--->服务器端口选择打开就可以啦

《C++ Primer》第9章 顺序容器(三)

参考资料: 《C Primer》第5版《C Primer 习题集》第5版 9.5 额外的string操作(P320) 9.5.1 构造string的其他方法 const char *cp "hello, world!"; char arr[] { h,\0,i,\0 }; string s1(cp); // s1 "hello, world!…

C#中的var究竟是强类型还是弱类型?

前言 在C#中,var关键字是用来声明变量类型的,它是C# 3.0推出的新特征,它允许编译器根据初始化表达式推断变量类型,有点跟javascript类似,而javascript中的var是弱类型。它让C#变量声明更加简洁,但也导致了…

算法设计与分析复习--分支界限法

文章目录 上一篇分支界限法性质装载问题0-1背包问题单源最短路问题最大团问题下一篇 上一篇 算法设计与分析复习–回溯法(二) 分支界限法性质 分支界限法是按广度优先策略或最小耗费优先遍历问题的解空间树。 搜索解空间: 子集树排列树 …

APP自动化之Poco框架

今天给大家介绍一款自动化测试框架Poco,其脚本写法非常简洁、高效,其元素定位器效率更快,其本质基于python的第三方库,调试起来也会非常方便,能够很好的提升自动化测试效率,节省时间。 (一)背景…

如何实现数据通过表格批量导入数据库

文章目录 1. 准备工作2. 创建数据库表3. 编写导入脚本4. 优化和拓展4.1 批量插入的优势4.2 错误处理4.3 数据验证4.4 数据转换 5. 总结 🎉如何实现数据通过表格批量导入数据库 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客&…

初学者必读书籍——两个月速成Python

想学Python的你是不是一直被它生涩难懂的劝退?作为一个自学入门的程序员,依靠这样几本书,两个月就学会了python。不卖关子,我学的就是”python编程三剑客“系列。那么接下来就让我给你介绍介绍吧。 1.《Python编程:从入…

OSG文字-osgText3D(5)

osgText3D 三维立体文字比二维平面文字显示效果更好,相对二维平面文字,它有非常好的立体显示效果。 在实际虚拟现实项目中,过多使用三维立体文字会降低染效率,加重渲染负担,相对平面二维文字,它占用的内存是…

掌握Katalon Studio 导入 swagger 接口文档,接口测试效率提升100%

katalon studio大家都已经不陌生了,是一款现在非常主流的自动化测试工具,包括了web、api、APP,甚至PC应用程序都可以使用它来完成自动化测试。 swagger是一款RESTFUL接口的文档在线自动生成软件,swagger是一个规范和完整的框架&a…