【多线程案例】阻塞式队列

news2024/9/24 1:24:56

1. 什么是阻塞式队列

阻塞队列是一种特殊的队列. 也遵守 "先进先出" 的原则. 阻塞队列能是一种线程安全的数据结构, 并且具有以下特性:

        当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素.

        当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素.

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

那什么是生产者消费者模型呢?

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等 待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取.

主要问题有两个

其一,用户端的“上游请求”的数量不是可控的,如果服务器A在同一时刻收到大量的支付请求,并且将请求直接交给B处理。这样服务器崩溃的概率是十分大的。

其二,两个服务器之间的耦合度太高了。A要调用B的话,A务必是知道B的存在的,如果B挂掉了很容易引起A的bug。此外现在需要再加一个服务器C也来处理工作,那么服务器A上的代码就需要重新部署。

引入阻塞式队列解决问题:

此时A,B不再有直接的任务接触,A讲请求放在阻塞式队列中,并不知道B的存在。B直接从阻塞式队列中拿“请求”,然后进行处理,B也不知道A的存在。这样一来就是一个简单的生产者消费者模型。

这样上面的两个问题就迎刃而解。如果现在“上游”用户的请求激增,阻塞式队列可以在AB之间提供一个缓冲区,B来不及处理的请求可以暂时在阻塞式队列中“存放”,从而达到了“削峰填谷”的目的。如果现在想要增加A或者B类型的服务器c,也不用修改原本的代码重新部署之类的操作,因为两者已经解耦。如果 B 挂了,对于 A 没有任何影响.因为队列还好着,A 仍然可以给队列插入元素.如果队列满,就先阻塞就好了如果 A 挂了,也对于 B 没啥影响.因为队列还好着,B 仍然可以从队列取元素.如果队列空,也就先阻塞就好了A B 任何一方挂了不会对对方造成影响!!!
新增一个 C 来作为消费者,对于 A 来说仍然是无感知的.

总结 生产者消费者模型的两个优点:

1)阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力.

2)阻塞队列也能使生产者和消费者之间 解耦.

2. 标准库中的阻塞式队列

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

        BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.

        put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.

        BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性

        LinkedBlockingDeque<Object> blockedQueue = new LinkedBlockingDeque<>();
        //入队列, 如果此时队列满 就回阻塞
        blockedQueue.put("zhangsan");
        // 出队列. 如果没有 put 直接 take, 就会阻塞.
        System.out.println(blockedQueue.take());

生产者消费者模型

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();
        Thread customer = new Thread(() -> {
            while (true) {
                try {
                    int value = blockingQueue.take();
                    System.out.println("消费元素: " + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者");
        customer.start();
        Thread producer = new Thread(() -> {
            Random random = new Random();
            while (true) {
                try {
                    int num = random.nextInt(1000);
                    System.out.println("生产元素: " + num);
                    blockingQueue.put(num);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();
        customer.join();
        producer.join();
    }
}

3. 写代码实现阻塞式队列

public class MyBlockingQueue {
    //通过循环队列的方式来实现
    private int[] elems = new int[1024];
    private volatile int size = 0;
    private int head = 0;
    private int tail = 0;

    public void put(int value) throws InterruptedException {
        synchronized (this) {
            //先判断put操作的时候 队列是否已经满了
            //注意使用while 而不是if 当线程再次被唤醒的时候,队列不一定就是没满
            while (size == elems.length){
                wait();
            }
            elems[tail] = value;
            tail = (tail + 1) % elems.length;
            size++;
            //成功插入一个新的元素后 就通知阻塞的take可以继续take了
            notifyAll();
        }
    }

    public int take() throws InterruptedException {
        int elem = 0;
        synchronized (this) {
            while(size == 0){
                wait();
            }
            elem = elems[head];
            head = (head + 1) % elems.length;
            size--;
            //成功拿走一个元素之后,就可以通知阻塞的put可以继续put了
            notifyAll();
        }
        return elem;
    }

    public synchronized int size() {
        return size;
    }

    // 测试代码
    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue blockingQueue = new MyBlockingQueue();
        Thread customer = new Thread(() -> {
            while (true) {
                try {
                    int value = blockingQueue.take();
                    System.out.println("消费:"+ value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者");
        customer.start();
        Thread producer = new Thread(() -> {
            Random random = new Random();
            while (true) {
                try {
                    int nextInt = random.nextInt(10000);
                    System.out.println("生产:" + nextInt);
                    blockingQueue.put(nextInt);
                    //让生产者慢一点 观察消费者
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();
        customer.join();
        producer.join();
    }

}

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

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

相关文章

性能工程落地的4个阶段

性能工程按照不同的内容和目的划分为4个阶段,分别是线下单系统压测分析阶段、线下全链路压测分析阶段、生产只读业务压测及容量评估阶段、生产读写业务全链路压测及容量评估阶段。(也可以理解为一个企业性能测试体系的发展阶段) 线下单系统压测分析阶段 针对单系统的性能…

第71步 时间序列建模实战:ARIMA建模(Python)

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们使用Python进行SARIMA模型的构建。 同样&#xff0c;这里使用这个数据&#xff1a; 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever wi…

LeetCode算法动态规划—剑指 Offer 10- II. 青蛙跳台阶问题

目录 剑指 Offer 10- II. 青蛙跳台阶问题 题解&#xff1a; 代码&#xff1a; 运行结果&#xff1a;​编辑 一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 答案需要取模 1e97&#xff08;1000000007&#xff…

实现按钮悬停动画

知识点与技巧 伪元素 使用伪元素来作为按钮悬停效果动画展示的元素 z-index 的使用技巧 使用z-index属性来控制按钮和伪元素的层次关系 transform、transition 复习 使用transform、transition两个属性来实现动画的展示 按钮边框动画 切换效果 核心代码 .btn.btn-border-…

Python语言开发环境安装

视频版教程 Python3零基础7天入门实战视频教程 首先去Python官网下载Python安装包 https://www.python.org/ 目前最新版本&#xff1a;Python 3.11.4 直接点击下载。&#xff08;建议用迅雷下载&#xff0c;速度快&#xff09; 双击安装包进行Python安装。 勾选下 Add pytho…

3D生成式AI模型、应用与工具大全

当谈到技术炒作时&#xff0c;人工智能正在超越虚拟世界&#xff0c;吸引世界各地企业和消费者的注意力。 但人工智能可以进一步增强虚拟世界&#xff0c;至少在某种意义上&#xff1a;资产创造。 AI 有潜力扩大用于虚拟环境的 3D 资产的创建。 推荐&#xff1a;用 NSDT编辑器…

二叉树(堆)

堆的性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b; 堆总是一棵完全二叉树。 大堆&#xff1a;任何父亲≥孩子 小堆&#xff1a;任何父亲≤孩子 接下来&#xff0c;我们要做的便是对堆进行增加和删除&#xff1a; 首先是增加操作&#xff0c…

【算法专题突破】滑动窗口 - 找到字符串中所有字母异位词(14)

目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后&#xff1a; 1. 题目解析 题目链接&#xff1a;438. 找到字符串中所有字母异位词 - 力扣&#xff08;Leetcode&#xff09; 这道题很好理解&#xff0c;就是找出从不同位置开始的所有异位词。 2. 算法原理 那我们该如…

下载安装python的详细步骤-2023

目录 一、介绍 二、下载 三、安装 四、测试 一、介绍 读者手册&#xff08;必读&#xff09;_csdn文章评分怎么看_云边的快乐猫的博客-CSDN博客 Python 是一种高级编程语言&#xff0c;具有简洁、易读、易学的特点。它由Guido van Rossum于1989年首次发布&#xff0c;并于1…

无涯教程-JavaScript - ACOTH函数

描述 ACOTH函数返回数字的反双曲余切。 语法 ACOTH (number)争论 Argument描述Required/OptionalNumberThe absolute value of Number must be greater than 1. i.e., Number must be must be less than -1 or greater than 1.Required Notes 用于计算双曲反余切的方程为-…

【第200篇原创文章】解决低于1%概率出现的芯片VPSS模块跑飞的问题

在发布SDK内测的时候&#xff0c;我们发现在切换视频分辨率的时候有低概率出现VPSS模块跑飞的情况&#xff0c;概率低于1%&#xff0c;试个两三百次&#xff0c;能出1~2次。切换视频分辨率这个功能在安防产品上也确实存在需求&#xff0c;网络带宽不大好的地方分辨率可以适当下…

边缘计算AI智能安防监控视频平台车辆违停算法详解与应用

随着城市车辆保有量呈现高速增长趋势&#xff0c;交通拥堵、违章行为也日益泛滥。因为车辆未停放在指定区域导致的车位浪费、占用/堵塞交通要道、车辆剐蹭等问题层出不穷。通过人工进行违法停车的监控&#xff0c;不仅让监控人员工作负荷越来越大&#xff0c;而且存在发现不及时…

文心一言:中文生成式对话产品

【产品介绍】 名称 文心一言上线/成立时间 2023年3月27日具体描述 文心一言&#xff08;英文名&#xff1a;ERNIE Bot&#xff09;是百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#xff0c;回答问…

pacman下载源失败怎么办

背景 编译c程序的时候&#xff0c;提示链接失败&#xff0c;错误如下&#xff1a; clang-15: error: linker command failed with exit code 1 (use -v to see invocation) [24/25] Linking CXX executable samples/xxx ninja: build stopped: subcommand failed.此处不过多介…

计算机竞赛 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

数学实验-圆周率π的计算(Mathematica实现)

一、实验名称&#xff1a;圆周率π的计算 二、实验环境&#xff1a;机房、Mathematica 10.3软件 三、实验目的&#xff1a;通过各种方法在Mathematica中计算圆周率π的值&#xff0c; 四、实验内容及结果 1 数值积分法计算π 计算定积分的数值&#xff0c;就得到了的值&am…

外包干了2个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

基于SSM+Vue的理发店会员管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

普中51-数码管实验

文章目录 数码管实验**静态数码管实验**动态数码管实验多位数码管简介数码管动态显示原理74HC245 和74HC138芯片介绍74HC245 芯片简介74HC138 芯片简介 代码如下&#xff1a; 数码管实验 如图所示&#xff1a; 从上图可看出&#xff0c;一位数码管的引脚是 10 个&#xff0c;…

path环境变量设置

path是一个包含多个路径的变量&#xff0c;用于指定系统可执行文件的搜索路径。主要作用是能够让系统快速启动一个应用程序。当用户输入一个命令时&#xff0c;系统会在path指定的路径中搜索可执行文件&#xff0c;以确定命令所对应的可执行文件位置。 如运行txt&#xff0c;tx…