【多线程】阻塞队列 详解

news2025/1/1 10:12:09

阻塞队列 详解

  • 一. 什么是阻塞队列
  • 二. 生产者消费者模型
  • 三. 标准库中的阻塞队列
  • 四. 阻塞队列实现

一. 什么是阻塞队列

阻塞队列是一种特殊的队列. 也遵守 “先进先出” 的原则.

阻塞队列是一种线程安全的数据结构, 并且具有以下特性:

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

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

二. 生产者消费者模型

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

生产者消费者模型的优点:

  1. 能对请求 “削峰填谷”

比如在 “秒杀” 场景下, 服务器同一时刻可能会收到大量的支付请求. 如果直接处理这些支付请求,
服务器可能扛不住(每个支付请求的处理都需要比较复杂的流程). 这个时候就可以把这些请求都放
到一个阻塞队列中, 然后再由消费者线程慢慢的来处理每个支付请求.
这样做可以有效进行 “削峰”, 防止服务器被突然到来的一波请求直接冲垮.

不使用阻塞队列:
在这里插入图片描述

使用阻塞队列:

在这里插入图片描述

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

比如过年一家人一起包饺子. 一般都是有明确分工, 比如一个人负责擀饺子皮, 其他人负责包. 擀饺子皮的人就是 “生产者”, 包饺子的人就是 “消费者”.
擀饺子皮的人不关心包饺子的人是谁(能包就行, 无论是手工包, 借助工具, 还是机器包), 包饺子的人也不关心擀饺子皮的人是谁(有饺子皮就行, 无论是用擀面杖擀的, 还是拿罐头瓶擀, 还是直接从超市买的).

不使用阻塞队列:
在这里插入图片描述

使用阻塞队列:

在这里插入图片描述

三. 标准库中的阻塞队列

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

  • BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
  • put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
  • BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// 入队列
queue.put("abc");
// 出队列. 如果没有 put 直接 take, 就会阻塞. 
String elem = queue.take();

生产者消费者模型

    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);
                    Thread.sleep(1000);
                } 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(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();
        customer.join();
        producer.join();
    }

四. 阻塞队列实现

  • 通过 “循环队列” 的方式来实现.
  • 使用 synchronized 进行加锁控制.
  • put 插入元素的时候, 判定如果队列满了, 就进行 wait. (注意, 要在循环中进行 wait. 被唤醒时不一定队列就不满了, 因为同时可能是唤醒了多个线程).
  • take 取出元素的时候, 判定如果队列为空, 就进行 wait. (也是循环 wait)
class MyBlockingQueue {
    private int[] items = new int[1000];
    private volatile int size = 0; // 注意使用 volatile 保证内存可见性
    private int head = 0;
    private int tail = 0;
    public void put(int value) throws InterruptedException {
        synchronized (this) {
            // 此处最好使用 while.
            // 否则 notifyAll 的时候, 该线程从 wait 中被唤醒,
            // 但是紧接着并未抢占到锁. 当锁被抢占的时候, 可能又已经队列满了
            // 就只能继续等待
            while (size == items.length) {
                wait();
            }
            items[tail] = value;
            tail = (tail + 1) % items.length;
            size++;
            notifyAll();
        }
    }
    public int take() throws InterruptedException {
        int ret = 0;
        synchronized (this) {
            while (size == 0) {
                wait();
            }
            ret = items[head];
            head = (head + 1) % items.length;
            size--;
            notifyAll();
        }
        return ret;
    }
    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);
                    Thread.sleep(1000);
                } 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(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();
        customer.join();
        producer.join();
    }
}

注意:

  1. 针对 this 加锁, 即针对当前类对象加锁, 只有多个线程使用同一个阻塞队列时才会发生阻塞。
  2. 使用 volatile 保证内存可见性。
  3. 判断是否需要 wait 时使用 while 循环判断。 因为可能线程被唤醒时, 条件又再次不满足了,
    比如: t1 线程取元素时发现队列空的就阻塞了, t2 插入了一个元素, 唤醒了所有的线程, 但是 t1 没有抢到锁, t2 抢到了, 把唯一的一个元素取走了, 当 t1 抢到锁时, 队列已经再次为空了,t1 还得继续阻塞等待。

好啦! 以上就是对 阻塞队列 的讲解,希望能帮到你 !
评论区欢迎指正 !

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

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

相关文章

freeswitch 播放远程文件的流程

freeswitch中既可以播放本地文件&#xff0c;也可以播放远程文件&#xff0c;如 loop_playback(1 https://xxxxxx/filename.wav) 即文件名以https:// 或者 http:// 开头。 流程图如下&#xff1a; 如何根据文件找对应的open回调函数呢&#xff1a; 用命令&#xff1a;show…

开利网络为祥兴集团营销团队提供驻场服务,共建数字化活力世界

近日&#xff0c;开利网络到访广西祥兴实业集团&#xff0c;参与全员营销会议&#xff0c;就集团目前如何推动业务增速、如何推动集团“活力世界”平台落地运营、共建营销激励机制进行落地沟通。 ​开利网络认为&#xff0c;集团目前存在营销目标分散、营销机制不清和营销产品混…

Apinto 网关: Go语言实现 HTTP 转 gRPC

gRPC 是由 Google 开发的一个高性能、通用的开源RPC框架&#xff0c;主要面向移动应用开发且基于 HTTP/2 协议标准而设计&#xff0c;同时支持大多数流行的编程语言。 gRPC 基于 HTTP/2 协议传输&#xff0c; HTTP/2 相比 HTTP1.x有以下优势: 采用二进制格式传输协议&#xff…

你真的会设计测试用例吗?

前言 最近干的最多的事情就是设计测试用例、评审测试用例了&#xff0c;于是我不禁又想到了一个经典的问题&#xff1a;如何设计出优秀的测试用例&#xff1f; 可能有些童鞋看到这个问题会有些不以为然&#xff0c;这有什么好想的&#xff1f;干个测试谁还不会设计测试用例&a…

IDEA插件的在线离线安装

插件的使用 插件的设置 在 IntelliJ IDEA 的安装讲解中我们其实已经知道&#xff0c;IntelliJ IDEA 本身很多功能也都是通过插件的方式来实现的&#xff0c;只是 IntelliJ IDEA 本身就是它自己的插件平台最大的开发者而已&#xff0c;开发了很多优秀的插件。 官网插件库&…

pdf怎么转换成ppt?可以试试这三种方法

pdf怎么转换成ppt&#xff1f;PDF和PPT是我们工作、学习中最为常见的文档格式之一&#xff0c;特别是PPT&#xff0c;几乎成为了商务汇报和学术演示的标配。有时候&#xff0c;我们需要将PDF文档转换成PPT格式以方便编辑和演示。那么&#xff0c;PDF怎么转换成PPT呢&#xff1f…

信息化建设第三方验收测试报告要求

信息化建设是企业未来高质量发展的必由之路&#xff0c;也是企业法治的必要手段。 信息化建设指品牌利用现代信息技术来支撑品牌管理的手段和过程。随着计算机技术、网络技术和通信技术的发展和应用&#xff0c;企业信息化已成为品牌实现可持续化发展和提高市场竞争力的重要保…

【UE 材质】力场护盾和冲击波效果

目录 效果 步骤 一、制作力场护盾材质 二、制作冲击波材质效果 三、制作冲击波粒子效果 四、制作震动效果 效果 步骤 一、制作力场护盾材质 1. 首先新建一个第一人称角色游戏模板 2. 新建一个材质&#xff0c;用于作为力场护盾的材质&#xff0c;这里命名为“Mat_for…

释放数据的潜力:用梯度上升法解锁主成分分析(PCA)的神奇

文章目录 &#x1f340;引言&#x1f340;什么是主成分分析&#xff08;PCA&#xff09;&#xff1f;&#x1f340;传统PCA vs 梯度上升PCA&#x1f340;PCA的优化目标&#x1f340;代码实现&#x1f340;求解第一主成分&#x1f340;求解第二主成分 &#x1f340;在sklearn中封…

数据结构:线性表之-循环双向链表(万字详解)

目录 基本概念 1&#xff0c;什么是双向链表 2&#xff0c;与单向链表的区别 双向链表详解 功能展示&#xff1a; 1. 定义链表 2&#xff0c;创建双向链表 3&#xff0c;初始化链表 4,尾插 5&#xff0c;头插 6&#xff0c;尾删 判断链表是否被删空 尾删代码 7&a…

怎么用excel管理固定资产

在当今的数字时代&#xff0c;我们已经习惯了使用各种电子工具来提高我们的生产力。其中&#xff0c;Excel无疑是一个强大的工具&#xff0c;它不仅可以帮助我们处理数据&#xff0c;还可以用来进行复杂的计算和分析。然而&#xff0c;你可能不知道的是&#xff0c;Excel也可以…

【已解决】src/spt_python.h:14:20: 致命错误:Python.h:没有那个文件或目录

src/spt_python.h:14:20: 致命错误&#xff1a;Python.h&#xff1a;没有那个文件或目录 问题 其中重点的报错信息 src/spt_python.h:14:20: fatal error: Python.h: No such file or directory 思路 sudo yum install python-devel然后重新安装需要的依赖。 解决 成功。…

DBeaver使用

一、导出表结构 二、导出数据CSV 导出数据时DBeaver并没有导出表结构&#xff0c;所以表结构需要额外保存&#xff1b; 导入数据CSV 导入数据时会因外键、字段长度导致失败&#xff1b;

4. HBase必知必会理论基础篇

HBase必知必会理论基础篇 1.1 HBase简介1.2 HBase 数据模型1.3 HBase整体架构1.4 HBase 读写流程1.4.1 客户端读取流程1.4.2 客户端写入流程 1.5 HBase 客户端常用的方法1.5.1 scan查询1.5.2 get查询1.5.3 put查询1.5.4 delete 查询1.5.5 append 查询1.5.6 increment查询 1.6 H…

从零开始在树莓派上搭建WordPress博客网站并实现公网访问

文章目录 序幕概述1. 安装 PHP2. 安装MySQL数据库3. 安装 Wordpress4. 设置您的 WordPress 数据库设置 MySQL/MariaDB创建 WordPress 数据库 5. WordPress configuration6. 将WordPress站点发布到公网安装相对URL插件修改config.php配置 7. 支持好友链接样式8. 定制主题 序幕 …

今天给大家分享一个绘图的 RGB COLOR TABLE

如果大家觉得有用&#xff0c;就点个赞让更多的人看到吧~

微信支付上新的“分分捐”很暖心,一起吗?

今天在买东西付款后发现&#xff0c;在付款页面发现下方出现了一个公益项目&#xff0c;名为“为乡村儿童送鸡蛋 去捐 0.01元”的提示。去查了一下才知道该功能名叫“分分捐”。 微信分分捐是什么&#xff1f; 当微信支付金额超过 5 元且为非整数的订单&#xff08;红包、转账…

如何激励员工?

如何激励员工&#xff1f;激励就是发现员工的需求&#xff0c;并满足它。 今天想给大家介绍下“马斯洛需求理论”&#xff0c;毕竟想要更好的激励下属创造价值&#xff0c;你首先需要了解他想要什么。对吧。 马斯洛需求理论是美国心理学家马斯洛在上世纪四十年代年提出的。他…

钢建筑模板和木建筑模板的优缺点?

当涉及到建筑模板材料时&#xff0c;钢模板和木模板都是常见的选择。它们各自具有一些优点和缺点&#xff0c;下面是钢模板和木模板的介绍&#xff1a; 钢模板的优点&#xff1a; 强度和稳定性&#xff1a;钢模板具有高强度和稳定性&#xff0c;能够承受较大的荷载和压力&…

人大女王金融硕士——不要成为群羊中盲从的羊,别人疯狂你要冷静

随着社会经济的迅速发展&#xff0c;经济全球化不断扩大&#xff0c;教育体系的完善。越来越多的人追求高学历&#xff0c;通过系统的学习来提升自己的知识储备&#xff0c;增长见识。这也导致每年考研成人逐年增加&#xff0c;录取率也逐年下降。很多选择考研的人&#xff0c;…