[JavaEE]阻塞队列

news2024/11/29 14:54:45


专栏简介: JavaEE从入门到进阶

题目来源: leetcode,牛客,剑指offer.

创作目标: 记录学习JavaEE学习历程

希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长.

学历代表过去,能力代表现在,学习能力代表未来


目录:

1.阻塞队列的概念

2.标准库中的阻塞队列

3.生产者消费者模型

3.1 生产者消费者模型的意义:

3.2 生产者消费者模型的作用:

 3.2 生产者消费者模型:

4.阻塞队列实现


1.阻塞队列的概念

阻塞队列是一种特殊的队列 , 也遵循"先进先出"的原则.

阻塞队列是一种线程安全的数据结构 , 具体特性如下:

  • 当队列满时 , 继续向队列中添加元素就会产生阻塞 , 直到其他线程从队列中取走元素.
  • 当队列空时 , 继续取队列中的元素就会产生阻塞 , 直到其他线程向队列中添加元素.

阻塞队列的一个典型应用场景就是"生产者消费者模型" , 是一种典型的开发方式.


2.标准库中的阻塞队列

Java标准库中内置了阻塞队列 , 如果我们需要在一些程序中使用阻塞队列 , 直接调用库即可.

阻塞队列队列是一个接口 , 具体的实现类是优先级阻塞队列 , 顺序表阻塞队列 , 链表阻塞队列.

public interface BlockingQueue<E> extends Queue<E> 

  • put方法用于阻塞式的入队列 , take方法用于阻塞式的出队列
  • 阻塞队列也有 poll() , offer() , peek()这些方法 , 但不具有阻塞性
BlockingQueue queue = new LinkedBlockingQueue();
        queue.put("hello");
        queue.take();

3.生产者消费者模型

3.1 生产者消费者模型的意义:

生产者消费者模型 , 本质上就是通过一个容器来解决生产者和消费者之间强耦合的问题.

通常意义下的生产者和消费者之间的耦合程度是很高的 , 如果生产者和消费者直接相关联 , 那么二者中任意一个出现错误就会导致另一个也出现错误. 如果要修改其中一个的代码 , 另一个也要修改不少代码 , 还要重新测试 , 重新发布 , 重新部署....非常麻烦.

那么如果我们使用"生产者消费者模型" , 生产者和消费者彼此之间不进行通信 , 而是通过一个阻塞队列来通信 , 所以生成者生成完数据不用直接给消费者 , 而是直接扔给阻塞队列.消费者也不再向生产者所要数据 , 而是直接从阻塞队列中取. 这样生产者和消费者之间的耦合程度就大大降低了.


3.2 生产者消费者模型的作用:

  • 1."削峰填谷" , 阻塞队列相当于一个缓冲区 , 平衡了生产者和消费者的处理能力.

在"秒杀"场景下 ,  服务器同一时刻可能收到大量的支付请求 , 如果直接处理这些请求 , 服务器可能会崩溃 , 这时如果把这些请求放入阻塞队列中 , 然后再由服务器来慢慢处理一个个请求 , 由此达到"削峰"的效果.

  • 2.阻塞队列也能使生成者和消费者之间"解耦".

比如一家人过年包饺子 , 这个流程需要由明确的分工 , 擀饺子皮的人是"生产者" , 包饺子的人是"消费者" , 擀饺子皮的人不必关心谁包饺子 , 包饺子的人也不必关心谁擀饺子皮.


 3.2 生产者消费者模型:

创建一个阻塞队列 , 一个生产者线程和一个消费者线程. 为了达到"生产一个 消费一个的效果" , 需要让生产者每生产一个就停顿一下.

public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
        Thread customer = new Thread(()->{
            try {
                while (true){
                    int ret = blockingQueue.take();
                    System.out.println("消费"+ret);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        Thread producer = new Thread(()->{
            int count = 0;
            while (true){
                try {
                    blockingQueue.put(count);
                    System.out.println("生产"+count);
                    count++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        customer.start();
        producer.start();
    }


4.阻塞队列实现

实现阻塞队列首先要实现一个队列 , 本文采用"循环队列的实现方式".

1) 实现循环队列

class MyBlockingQueue{
    public int head;
    public int tail;
    public int size;
    public int[] queue = new int[1000];
    //入队
    public void put(int value){
        if(size == queue.length){
            return;
        }
        queue[tail] = value;
        tail++;
        if(tail>=queue.length){
            tail = 0;
        }
        size++;
    }
    //出队
    public Integer take(){
        if(size == 0){
            return null;
        }
        int ret = queue[head];
        head++;
        if(head>=queue.length){
            head = 0;
        }
        size--;
        return ret;
    }
}

2) 改进为阻塞队列

在循环队列的基础上 , 入队时发现队伍满了 , 就使用 wait 阻塞. 出队时发现队伍空了 , 也用 wait 阻塞. 当入队操作阻塞时 , 如果出队操作执行完毕就可以通知入队操作解除阻塞. 当出队操作阻塞时 , 如果入队操作执行完毕就可以通知出队操作解除阻塞.

  • 那么是否会出现入队和出队操作同时陷入阻塞的情况?

答案是不会 , 因为入队和出队被同步代码块限定为同一个队列 ,  同一个队列不可能出现即空又满的情况 , 否则就会成为薛定谔的队列.

  •  那么是否会出现入队的 wait 被唤醒 , 队伍还是满或出队的 wait 被唤醒队伍还是空的情况?

理论上我们实现的代码不会产生这个问题 , 但为了保险起见 , 我们要将返回的 wait 再判定一次 , 看此时的条件是否具备.因此我们可以参考 Java 标准库中的 wait , 使用循环判定. 

class MyBlockingQueue{
    public int head = 0;
    public int tail = 0;
    public int size = 0;//表示元素个数
    public int[] arrQ = new int[1000];

    //入队 put

    public void put(int value) throws InterruptedException {
        synchronized (this) {
            while (size == arrQ.length ){//为了防止出现wait被唤醒还是满的情况.符合标准库的规范
               //队列满了 , 产生阻塞
                this.wait();
            }
            arrQ[tail] = value;
            tail++;
            if(tail>=arrQ.length){
                tail=0;
            }
            size++;
            //这个notify take()中的wait
            this.notify();
        }
    }
    //出队 take
    public Integer take() throws InterruptedException {
        int ret;
        synchronized (this) {
            while (size == 0){
               this.wait();
            }
            ret = arrQ[head];
            head++;
            if(head>=arrQ.length){
                head=0;
            }
            size--;
            //唤醒 put() 中的wait
            this.notify();
        }
        return ret;
    }
}

测试自定义阻塞队列: 

public class ThreadDemo6 {
    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue();
        //创建两个线程来作为生产者和消费者
        Thread customer = new Thread(()->{
            while (true){
                try {
                    int result = myBlockingQueue.take();
                    System.out.println("消费"+result);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        customer.start();
        Thread producer = new Thread(()->{
            int count = 0;
            while (true){
                try {
                    System.out.println("生产"+count);
                    myBlockingQueue.put(count);
                    count++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        producer.start();
    }
}

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

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

相关文章

1999-2019年全国、各省市直辖区居民收入和消费支出情况面板数据

1999-2019年全国、各省市直辖区居民收入和消费支出情况面板数据 1、时间&#xff1a;1999-2019年 2、指标&#xff1a; 可支配收入、城镇居民家庭平均每人全年消费性支出、食品消费支出、医疗保健消费支出、农村居民家庭人均纯收入、农村居民家庭平均每人生活消费支出、食品…

【Unity URP】设置光源层Light Layers

光源层 (Light Layers) 功能允许配置某些光源仅影响特定的游戏对象。 此功能可以用于加亮在暗处的物体。 1.开启光源层&#xff0c;并设置光源层名称 在URP资源中&#xff0c;点击Lighting右侧的垂直省略号图标 (⋮)&#xff0c;勾选Show Additional Properties&#xff0c…

【已解决】WARNING: Ignoring invalid distribution xxx

问题解决方案解释问题 WARNING: Ignoring invalid distribution -umpy (c:\users\xxx\appdata\roaming\python\python36\site-packages) 解决方案 在报错的路径下(c:\users\xxx\appdata\roaming\python\python36\site-packages)&#xff0c;找到~对应文件夹&#xff0c;此处…

Pytorch实战笔记(1)——BiLSTM 实现情感分析

本文展示的是使用 Pytorch 构建一个 BiLSTM 来实现情感分析。本文的架构是第一章详细介绍 BiLSTM&#xff0c;第二章粗略介绍 BiLSTM&#xff08;就是说如果你想快速上手可以跳过第一章&#xff09;&#xff0c;第三章是核心代码部分。 目录1. BiLSTM的详细介绍2. BiLSTM 的简单…

【三年面试五年模拟】算法工程师的独孤九剑秘籍(第十二式)

Rocky Ding公众号&#xff1a;WeThinkIn写在前面 【三年面试五年模拟】栏目专注于分享AI行业中实习/校招/社招维度的必备面积知识点与面试方法&#xff0c;并向着更实战&#xff0c;更真实&#xff0c;更从容的方向不断优化迭代。也欢迎大家提出宝贵的意见或优化ideas&#xff…

【算法】二叉树

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录二叉树数组转化为二叉树二叉树转化为二叉链表二叉树的遍历排序二叉树BST&#xff08;二叉搜索树&…

新思路dp

参考文章思路&#xff1a;点我 题&#xff1a;C. Count Binary Strings 前言&#xff1a;嗯,今天做这个题的时候&#xff0c;想了一堆乱七八糟的解法&#xff0c;想记录一下hhhhhh。 题意&#xff1a;输入以类似于邻接表的形式给出字符串&#xff08;只由000和111组成&#…

深入理解MySQL中的bin log、redo log、undo log

bin log&#xff08;二进制日志&#xff09; 什么是bin log&#xff1f; 记录数据库执行的写入性操作信息&#xff0c;以二进制的形式保存在磁盘中。 由服务层产生&#xff0c;所有储存引擎都支持。 bin log属于逻辑日志。 bin log日志有三种格式&#xff1a;STATMENT、ROW、M…

详解数据库的锁机制及原理

详解数据库的锁机制及原理1.数据库锁的分类2.行锁共享锁排他锁更新锁3.意向锁4.锁机制解释数据库隔离级别1.数据库锁的分类 本图源自CSDN博主&#xff1a;Stephen.W 数据库锁一般可以分为两类&#xff0c;一个是悲观锁&#xff0c;一个是乐观锁 乐观锁一般是指用户自己实现的…

Java 中是如何获取 IP 属地的

细心的小伙伴可能会发现&#xff0c;抖音新上线了 IP 属地的功能&#xff0c;小伙伴在发表动态、发表评论以及聊天的时候&#xff0c;都会显示自己的 IP 属地信息下面&#xff0c;我就来讲讲&#xff0c;Java 中是如何获取 IP 属地的&#xff0c;主要分为以下几步 通过 HttpSer…

HuggingFace (transformers) 自定义图像数据集、使用 DeiT 模型、Trainer 进行训练回归任务

资料 Hugging Face 官方文档&#xff1a;https://huggingface.co/ Hugging Face 代码链接&#xff1a;https://github.com/huggingface/transformers 1. 环境准备 创建 conda 环境激活 conda 环境下载 transformers 依赖下载 transformers 中需要处理数据集的依赖下载 pytor…

win10录屏文件在哪?如何找到录制后的文件

在工作和学习中&#xff0c;我们会遇到需要使用录屏工具录制电脑屏幕的情况&#xff0c;很多小伙伴在录制完win10电脑屏幕之后&#xff0c;找不到录制的视频文件。win10录屏文件在哪&#xff1f;今天小编教大家如何找到电脑录屏文件和录制win10电脑屏幕的方法&#xff0c;如果您…

带你认识QOwnNotes

导读QOwnNotes 是一款自由而开源的笔记记录和待办事项的应用&#xff0c;可以运行在 Linux、Windows 和 mac 上。这款程序将你的笔记保存为纯文本文件&#xff0c;它支持 Markdown 支持&#xff0c;并与 ownCloud 云服务紧密集成。 QOwnNotes 的亮点就是它集成了 ownCloud 云服…

数据量大也不卡的bi软件有哪些?

用过数据分析软件的都知道&#xff0c;很多的软件在数据量不算特别大的时候还好&#xff0c;分析效率、响应速度都不慢&#xff0c;但一旦使用的数据量超过一定范围&#xff0c;系统就会明显变慢&#xff0c;甚至崩溃。随着企业业务的发展扩张&#xff0c;数据分析的精细化&…

Linksys WRT路由器刷入OpenWrt与原厂固件双固件及切换

Linksys路由器OpenWrt与原厂固件双固件刷入及切换双固件机制使用原厂固件刷其他固件使用原厂固件切换启动分区使用OpenWrt刷入Sysupgrade使用OpenWrt刷入Img使用OpenWrt切换分区通用的硬切换分区&#xff08;三次重启&#xff09;双固件机制 新机器默认有一个原厂固件&#xf…

详解分布式系统核心概念——CAP、CP和AP

最近研究Sykwalking&#xff0c;当调研 oap如何进行集群部署时发现&#xff1a;skywalking oap 之间本身不能搭建集群&#xff0c;需要一个集群管理器来组建集群&#xff0c;它支持nacos、zookeeper、Kubernetes、Consul、Etcd 五种集群管理器。我重点比较了nacos和zookeeper&a…

python中的闭包和装饰器

目录 一.闭包 1.闭包的用途和用法 简单闭包 2.nonlocal关键字的作用 ATM闭包实现 注意事项 小结 二.装饰器 装饰器的一般写法&#xff08;闭包写法&#xff09; 装饰器的语法糖写法 一.闭包 1.闭包的用途和用法 先看如下代码&#xff1a; 通过全局变量account_amount来…

【Python学习】条件和循环

前言 往期文章 【Python学习】列表和元组 【Python学习】字典和集合 条件控制 简单来说&#xff1a;当判断的条件为真时&#xff0c;执行某种代码逻辑&#xff0c;这就是条件控制。 那么在讲条件控制之前&#xff0c;可以给大家讲一个程序员当中流传的比较真实的一个例子…

CUDA规约算法(加和)

1.block内相邻元素规约&#xff08;线程不连续&#xff09; 上图为1个block内的16个线程的操作示意&#xff1a; 第0个线程会和第1&#xff0c;2&#xff0c;4&#xff0c;8发生关系 第2个线程会和第3个线程发生关系 第4个线程会和第5&#xff0c;6个线程发生关系 ... 以上…

这7个网络设备配置接口基本参数要牢记,从此接口相关配置不用怕!

本文给大家介绍网络设备配置接口基本参数&#xff0c;包括接口描述信息、接口流量统计时间间隔功能以及开启或关闭接口。 进入接口视图 背景信息 对接口进行基本配置前&#xff0c;需要进入接口视图。 操作步骤 执行命令system-view&#xff0c;进入系统视图。执行命令inte…