【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 {
            //如果队列为空,则返回null
            if (size == 0){
                return null;
            }

            //取出队首元素
            String elem = array[head];
            //如果head已经到了队尾,那么下一个置0
            if(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();
    }
}

运行结果如图

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot实现摄影跟拍预定管理系统【项目源码+论文说明】

基于springboot实现摄影跟拍预定管理系统演示 摘要 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要…

SD3403/SS928 视频采集调试记录

问题1:运行 ./sample_vio 0 报错 问题2:板卡连接摄像模组后无法ping同 错误原因: 摄像头模组连接错误 修改后重新调试 下一步 外接显示屏查看输入输出 ...

产品工程师工作的职责十篇(合集)

一、岗位职责的作用意义 1.可以最大限度地实现劳动用工的科学配置; 2.有效地防止因职务重叠而发生的工作扯皮现象; 3.提高内部竞争活力,更好地发现和使用人才; 4.组织考核的依据; 5.提高工作效率和工作质量; 6.规范操作行为; 7.减少违章行为和违章事故的发生…

FlinkCDC实现主数据与各业务系统数据的一致性(瀚高、TIDB)

文章末尾附有flinkcdc对应瀚高数据库flink-cdc-connector代码下载地址 1、业务需求 目前项目有主数据系统和N个业务系统,为保障“一数一源”,各业务系统表涉及到主数据系统的字段都需用主数据系统表中的字段进行实时覆盖,这里以某个业务系统的一张表举例说明:业务系统表Ta…

9. BeanFactory 和 ApplicationContext有什么区别?

BeanFactory 和 ApplicationContext有什么区别? BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是 BeanFactory的子接口。 依赖关系 BeanFactory:是Spring里面最顶层的接口&#…

springboot宠物领养系统-计算机毕设 附源码 44261

springboot宠物领养系统 摘 要 网络发布信息有其突出的优点,即信息量大,资源丰富,更新速度快等,很符合人们希望以捷、便利的方式获得最多最有效信息的要求。本系统就是一个网上宠物领养系统,为宠物爱好者提供一个信息…

2023-11-20 LeetCode每日一题(最大子数组和)

2023-11-20每日一题 一、题目编号 53. 最大子数组和二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的…

Motion v5.6.7 苹果电脑上的视频编辑

Motion mac是一款运行在苹果电脑上的视频编辑软件,它能让您自定Final Cut Pro字幕、转场和效果。 它可以在2D或3D空间中创建您自己的精美炫目的动画,同时还能在您工作时提供实时反馈。广色域支持让你的动态图形更显出色光彩。3D文字功能经过优化增强&am…

梨花声音研修院,严肃与刚毅是音色核心

在为军旅剧提供配音服务时,配音员需捕捉并展现军事场合的严肃气氛、军人的刚毅品质以及他们对职责的忠诚。军旅剧往往围绕着军人的日常生活、战场经历、战友之情以及对祖国的热爱等主题展开,所以配音需能传递这些情感和价值。以下是进行军旅剧配音的一些…

游戏开发引擎Cocos Creator和Unity如何对接广告-AdSet聚合广告平台

在游戏开发方面,游戏引擎的选择对开发过程和最终的产品质量有着重大的影响,Unity和Cocos是目前全球两大商用、通用交互内容开发工具,这两款引擎受到广泛关注,本文将从多个维度对两者进行比较,为开发者提供正确的选择建…

性能测试工具有哪些?原理是什么?怎么选择适合的工具?

前言 本篇文章主要简单总结下性能测试工具的原理以及如何选型。性能测试和功能测试不同,性能测试的执行是基本功能的重复和并发,需要模拟多用户,在性能测试执行时需要监控指标参数,同时性能测试的结果不是那么显而易见&#xff0c…

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

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

vr编辑器可以解决教育教学中的哪些问题

VR编辑器是一种基于虚拟现实技术的教育内容编辑器,可以帮助教师快速创建出高质量的虚拟现实教学内容。 比如在畜牧教学类,通过这个软件,教师可以将真实的动物场景、行为和特征模拟到虚拟现实环境中,让学生在沉浸式的体验中学习动物…

智能监控如何最大化保障生产工人权益,助力电焊车间智能化?

电焊车间加装监控可以加强对电焊车间的生产过程监控,保障员工的生产工作安全,提高工作效率,降低生产成本。但是传统的监控只能单一的去“看”,并不能最大化发挥视频监控的作用,而智能视频监控就不一样。它可以有效提高…

wvp分享视频访问页面

先登录查看视频 输入用户名密码登录 国标设备--点击通道 点击播放 点击复制 打开分享链接查看视频 直接在浏览器中打开 可以直接预览 原有标签退出登录 刷新分享的视频链接依然可以查看视频 iframe内嵌网页查看视频 获取iframe代码 点击复制 打开vscode,新建一…

Python——练习2

Python 练习一练习二练习三 练习一 (回文素数)回文素数是指一个数既是素数又是回文数。例如,131 既是素数也是回文数。数字313和717都是如此。编写程序显示前 100 个回文素数。每行显示10个数字,并且准确对齐如下所示。 2 3 5 7 11 …

DependencyProperty.Register:wpf 向别的xaml传递参数

一.使用背景:在A.xaml中嵌入B.xaml,并且向B.xaml传递参数。 函数介绍: public static DependencyProperty Register(string name, Type propertyType, Type ownerType );name(string): 依赖属性的名称。在…

二、Gitee使用方法

目录 (1)首先可以注册一个 gitee 账号,注册很方便,自行注册 (2)登陆后进入你的主页 (3)创建仓库 (3)克隆 (4)代码提交 &#xf…

IOS+Appium+Python自动化全实战教程

由于公司的产品坐落于不同的平台,如ios、mac、Android、windows、web。因此每次有新需求的时候,开发结束后,留给测试的时间也不多。此外,一些新的功能实现,偶尔会影响其他的模块功能正常的使用。 网上的ios自动化方面的…

alova—轻量级请求策略库

文章目录 前言alova 是什么为什么创造 alova 一、选择 alova 的理由?二、使用步骤完整的特性列表alova 请求策略表 三、如何使用安装使用 useRequest 发送一个请求 总结alova和请求库的关系 前言 Alova官网 Alova—github官网 alova 是什么 alova 是一个轻量级的…