【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.hqwc.cn/news/206540.html

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

相关文章

编译源码-【opencv3.4.16 + vs2013 x64】

编译机器:i5 13500HX RTX 4050 laptop win11 CMake 3.26.4 Configure,去掉勾选图中黄色标注的项,opencv_world 随意 Configure可能提示3rdparty下载timeout,它会下载到源码目录的.cache ├── .cache │ ├──ffmpeg │ │ …

易点易动设备管理系统:提升企业设备保养效率的最佳选择

在现代企业中,设备的正常运行和保养对于业务的顺利进行至关重要。然而,传统的手动设备管理方式往往效率低下、容易出错,给企业带来不必要的成本和风险。为了解决这一问题,易点易动设备管理系统应运而生。本文将介绍易点易动设备管…

【操作系统】I/O软件层次结构

文章目录 1. 前言2. I/O软件层次结构2.1 用户层软件2.2 设备独立性软件2.3 设备驱动程序2.4 中断处理程序 1. 前言 偶然看到“程序员的护城河是什么”这个话题,作为一个工作两年多的程序员吧,经常看到网上关于各种35岁危机、裁员甚至猝死之云云。最近也…

数据结构题型

选择题 2021 数据处理的单位:数据元素 矩阵压缩存储 2022 ①单链表头插法选择 ②矩阵压缩存储,行优先 ③删除链表节点的时间复杂度 ④稀疏矩阵存储 ⑤平衡二叉树时间复杂度 ⑥栈和队列的出队,问栈的大小至少多少 ⑦拓扑排序 ⑧参考书 360…

软件第三方测评报告可作哪些用途?

软件第三方测评报告是指由独立、中立的第三方机构对软件进行全面、客观、科学的评估和分析后所做的报告。该报告基于系统而严密的评测流程,通过多项指标和标准,对软件的性能、功能、易用性、安全性等方面进行评价,为用户提供一个权威、可靠的…

js读取json文件

1. 原生的两种方法 1.1 XMLHttpRequest const xhr new XMLHttpRequest(); xhr.overrideMimeType("application/json"); xhr.open(GET, data.json, true); xhr.onreadystatechange function () {if (xhr.readyState 4 && xhr.status 200) {const data …

【必读】从零开始,一步步教你安装nginx,搭建个人博客网站!

nginx搭建个人网站 Nginx是一款轻量级Web服务器、反向代理服务器以及电子邮件代理服务器,并且具有高并发连接处理能力和低内存消耗的特点。它也可以用于负载均衡和缓存控制等功能。 功能: 静态网站服务器:Nginx可以用来作为静态网站服务器&am…

d3dcompiler_47.dll缺失怎么修复,d3dcompiler_47.dll的作用有哪些

d3dcompiler_47.dll丢失是一种常见的电脑问题。如果你遇到了这个问题,不要惊慌,下面的方法可以帮助你解决。本文将详细介绍解决d3dcompiler_47.dll丢失问题的步骤,让你手把手地学会。 一.解决d3dcompiler_47.dll丢失问题的步骤 解决方法一&a…

【问题定位】通过看Mybatis源码解决系统问题

开发需求好好的,运维同事突然发现了一个问题,某个任务的详情页面加载不出来。看日志,系统在进行查询操作的时候抛出空指针异常。感觉是Mybatis内部异常,所以就跟踪源码看下Mybatis运行到哪一步报错的。 DefaultSqlSession#select…

SQLite3 数据库学习(四):Qt 数据库基础操作

参考引用 SQLite 权威指南&#xff08;第二版&#xff09;SQLite3 入门 1. 创建连接执行 sql 语句 在 Qt 中使用数据库要在工程文件中添加QT sql1.1 main.cpp #include "createsqlapp.h" #include <QApplication> #include <QSqlDatabase> #include &l…

技术分享| gcc版本升级到5.2

一、介绍 GCC&#xff08;GNU Compiler Collection&#xff09;是一套广泛使用的开源编译器集合&#xff0c;用于编译多种编程语言&#xff0c;包括C、C、Objective-C、Fortran等。GCC 的不同版本提供了许多新功能、改进和修复&#xff0c;其中包括从 GCC 4.8.5 升级到 GCC 5.…

Python Pandas简介及基础教程+实战示例。

文章目录 前言一、Pandas简介二、Python Pandas的使用关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前言 Pan…