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

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

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

 运行结果:


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

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

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

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

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

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

 

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

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

相关文章

喜报 | 通付盾WAAP解决方案入选国家工业信息安全发展研究中心“2023年数字化转型自主创新解决方案优选案例”

为提升自主创新产品质量和技术创新能力,助力重点行业自主可控基础设施建设,加速重点行业数字化转型工作进程,促进重点行业产业链数字化升级,推动重点行业数字化、网络化、智能化发展。国家工业信息安全发展研究中心联合中国交通建…

SQL手工注入漏洞测试(MySQL数据库-字符型)-墨者

———靶场专栏——— 声明:文章由作者weoptions学习或练习过程中的步骤及思路,非正式答案,仅供学习和参考。 靶场背景: 来源: 墨者学院 简介: 安全工程师"墨者"最近在练习SQL手工注入漏洞&#…

SpringBoot——Quartz 定时任务

优质博文&#xff1a;IT-BLOG-CN 一、Scheduled 定时任务 【1】添加Scheduled相关依赖&#xff0c;它是Spring自带的一个jar包因此引入Spring的依赖&#xff1a; <dependency><groupId>org.springframework</groupId><artifactId>spring-context-su…

对于Web标准以及W3C的理解、对viewport的理解、xhtml和html有什么区别?

1、对于Web标准以及W3C的理解 Web标准 Web标准简单来说可以分为结构、表现、行为。 其中结构是由HTML各种标签组成&#xff0c;简单来说就是body里面写入标签是为了页面的结构。 表现指的是CSS层叠样式表&#xff0c;通过CSS可以让我们的页面结构标签更具美感。 行为指的是…

2023年12月02日新闻简报(国内国际)

新闻简报 每天三分钟&#xff0c;朝闻天下事。今天是&#xff1a;2023年12月02日&#xff0c;星期六&#xff0c;农历十月廿十&#xff0c;祝工作愉快&#xff0c;身体健康&#xff0c;生活喜乐&#xff1a;&#xff1a; 国内新闻 1、商务部&#xff1a;对原产于澳大利亚的进…

【C指针】深入理解指针(最终篇)数组指针指针运算题解析(一)

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

电商营销场景的RocketMQ实战01-RocketMQ原理

架构图 Broker主从架构与集群模式 RocketMQ原理深入剖析 Broker主从架构原理 HAConnection与HAClient Broker基于raft协议的主从架构 Consumer运行原理 基础知识 001_RocketMQ架构设计与运行流程分析 RocketMQ这一块&#xff0c;非常关键的一个重要的技术&#xff0c;面试的时候…

【Vue3+Ts项目】硅谷甄选 — 搭建后台管理系统模板

一、 项目初始化 一个项目要有统一的规范&#xff0c;需要使用eslintstylelintprettier来对我们的代码质量做检测和修复&#xff0c;需要使用husky来做commit拦截&#xff0c;需要使用commitlint来统一提交规范&#xff08;即统一提交信息&#xff09;&#xff0c;需要使用pre…

Day04:每日一题:2661. 找出叠涂元素

2661. 找出叠涂元素 给你一个下标从 0 开始的整数数组 arr 和一个 m x n 的整数 矩阵 mat 。 arr 和 mat 都包含范围 [1&#xff0c;m * n] 内的 所有 整数。从下标 0 开始遍历 arr 中的每个下标 i &#xff0c;并将包含整数 arr[i] 的 mat 单元格涂色。请你找出 arr 中在 mat…

ubuntu系统下搭建本地物联网mqtt服务器的步骤

那么假如我们需要做一些终端设备&#xff0c;例如温湿度传感器、光照等物联网采集设备要接入呢&#xff1f;怎么样才能将数据报送到服务器呢&#xff1f; 以下内容基于我们ubuntu系统下的emqx成功启动的基础上。我们可以用浏览器键入控制板的地址&#xff0c;如果启动成功&…

【数电笔记】基本和复合逻辑运算

说明&#xff1a; 笔记配套视频来源&#xff1a;B站 基本逻辑运算 1. 与运算 &#xff08;and gate&#xff09; 2. 或运算 &#xff08;or gate&#xff09; 3. 非运算 &#xff08;not gate &#xff09; 复合逻辑运算 1. 与非运算&#xff08;nand&#xff09; 2. 或非运…

机器人仿真系统调研

仿真是机器人学习和研究过程中最重要的工具之一&#xff0c;是指通过计算机对实际的物理系统进行模拟的技术。机器人仿真基于交互式计算机图形技术和机器人学理论&#xff0c;生成机器人的几何图形&#xff0c;并对其进行三维显示&#xff0c;用来描述机器人及工作环境的动态变…

九章量子计算机:探索量子世界的革命性工具

九章量子计算机:探索量子世界的革命性工具 一、引言 九章量子计算机的推出,是近年来科技界最为引人瞩目的成就之一。这款基于量子力学的计算机,以其独特的计算方式和潜在的应用前景,引发了全球范围内的关注和讨论。本文将深入探讨九章量子计算机的原理、技术特点、应用前景…

计算机硬件(二)

1.内存和内存条的用途 假设运行内存16个g,能开多少软件 后台和前台能同时运行多少APP RAM越大越好 464 6128 8128 10256 例子: 8gx216g 两根内存条 16g运行内存 2.内存频率的用途 DDR5 DDR4 DDR3 后面的数字越大,越好,可以理解为传输的速度&#xff0c;内存搭配主…

小米智能摄像头mp4多碎片手工恢复案例

小米智能摄像头mp4多碎片手工恢复案例 智能摄像头目前在市场上极为常见&#xff0c;仅需要一张存储卡即可实现视频、音频的采集&#xff0c;同时可以通过手机APP进行远程控制&#xff0c;相比传统安防品牌成本更低、更容易部署。在智能摄像头品牌中小米算是绝对的大厂&#xf…

ES-深入理解倒排索引

倒排索引 idproductdesc1新版 小米 至尊-纪念版手机1小米 NFC 手机3NFC手机4小米 耳机5华为 耳机6扫地机器人7华为 Mata………………term_indexterm dictionaryposting list------------------------------------小米1……100W华为6,7,9NFC76,90耳机5352红米643,98机器人645,9…

数字电源为什么一般用DSP控制,而不能用普通的单片机?

数字电源为什么一般用DSP控制&#xff0c;而不能用普通的单片机&#xff1f; 首先你要清楚&#xff0c;数字电源需要一个芯片具备什么功能&#xff1f; 1 能发PWM波 &#xff0c;并且具备保护关断功能&#xff1b; 电源对PWM发波 要求很高&#xff0c;精度要ns级甚至ps级的&…

C++中异常的栈展开概念

C中的异常栈展开是指&#xff0c;当某个函数中有异常产生&#xff08;这里不考虑是主动抛出的还是被动产生的&#xff09;&#xff0c;在异常被捕获之前的函数调用链上&#xff0c;函数不会正常执行返回&#xff0c;即异常产生之后的程序逻辑不会被执行。 &#xff08;注意&…

RTDETR阅读笔记

RTDETR阅读笔记 摘要 DETR的高计算成本限制了它们的实际应用&#xff0c;并阻碍了它们充分利用无需后处理&#xff08;例如非最大抑制NMS&#xff09;的优势。文中首先分析了NMS对实施目标检测的精度和速度的负面影响。&#xff08;RTDETR是第一个实时端到端的目标检测器。具…

temu货不对板哪里修改图片

在Temu这个跨境电商平台上&#xff0c;如果您需要修改商品图片&#xff0c;通常需要在卖家中心进行操作。下面是一般的步骤&#xff0c;但请注意&#xff0c;不同平台的操作可能略有不同&#xff0c;具体请参考Temu官方的帮助文档或联系客服。 先给大家推荐一款拼多多/temu运营…