多线程-阻塞队列(超详细)

目录

1.阻塞队列是什么

 生产者-消费者模型

2.标准库中的阻塞队列

 ⽣产者-消费者模型 

 阻塞队列实现


1.阻塞队列是什么

         阻塞队列(Blocking Queue)是一种特殊类型的队列,它在插入和删除元素时可以提供阻塞机制。阻塞队列能是⼀种线程安全的数据结构, 并且具有以下特性:

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

生产者-消费者模型

      阻塞队列的⼀个典型应⽤场景就是 "⽣产者消费者模型". 这是⼀种⾮常典型的开发模型. ⽣产者消费者模式就是通过⼀个容器来解决⽣产者和消费者的强耦合问题。 ⽣产者和消费者彼此之间不直接通讯,⽽通过阻塞队列来进⾏通讯,所以⽣产者⽣产完数据之后不⽤ 等待消费者处理,直接扔给阻塞队列,消费者不找⽣产者要数据,⽽是直接从阻塞队列⾥取.

  1. 阻塞队列就相当于⼀个缓冲区,平衡了⽣产者和消费者的处理能⼒. (削峰填⾕)

      就像三峡大坝一样。它是为了抵御突然的洪水修建的。洪水突然来临的时候,三峡大坝可以缓解巨大的洪水量,将它储蓄起来。经过三峡大坝缓冲后,流到下流的水就不会特别大。当长时间干旱没有下雨的时候,三峡大坝就可以开闸放水,来填补。

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

      ⽐如过年⼀家⼈⼀起包饺⼦. ⼀般都是有明确分⼯, ⽐如⼀个⼈负责擀饺⼦⽪, 其他⼈负责包. 擀饺⼦⽪的⼈就是 "⽣产者", 包饺⼦的⼈就是 "消费者". 擀饺⼦⽪的⼈不关⼼包饺⼦的⼈是谁(能包就⾏, ⽆论是⼿⼯包, 借助⼯具, 还是机器包), 包饺⼦的⼈也 不关⼼擀饺⼦⽪的⼈是谁(有饺⼦⽪就⾏, ⽆论是⽤擀⾯杖擀的, 还是拿罐头瓶擀, 还是直接从超市买的)

2.标准库中的阻塞队列

       在 Java 标准库中内置了阻塞队列. 如果我们需要在⼀些程序中使⽤阻塞队列, 直接使⽤标准库中的即可.
BlockingQueue 是⼀个接⼝. 真正实现的类是 LinkedBlockingQueue.
put ⽅法⽤于阻塞式的⼊队列, take ⽤于阻塞式的出队列.
BlockingQueue 也有 offer, poll, peek 等⽅法, 但是这些⽅法不带有阻塞特性.
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// ⼊队列
queue.put("abc");
// 出队列. 如果没有 put 直接 take, 就会阻塞. 
String elem = queue.take();

   ⽣产者消费者模型 

        生产者线程使用随机数生成器随机产生元素,并将其放入阻塞队列中。如果队列已满,则生产者线程会阻塞等待,直到有空闲位置可供插入。消费者线程从阻塞队列中获取元素,并进行相应的处理。如果队列为空,则消费者线程会阻塞等待,直到有数据可供消费。

        通过使用阻塞队列,生产者和消费者之间实现了解耦,避免了线程之间的竞争和忙等待,提高了系统的效率和性能。同时,阻塞队列提供了线程安全的插入和移除操作,保证了数据的有序处理和线程间的协调。

        在主函数中,启动了一个生产者线程和一个消费者线程,并使用 join() 方法等待它们的执行完成。这样可以确保程序在所有线程都执行完毕后退出,避免产生未处理的异常。

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

 阻塞队列实现

通过 "循环队列" 的⽅式来实现.
使⽤ synchronized 进⾏加锁控制.
put 插⼊元素的时候, 判定如果队列满了, 就进⾏ wait. (注意, 要在循环中进⾏ wait. 被唤醒时不⼀定
队列就不满了, 因为同时可能是唤醒了多个线程).
take 取出元素的时候, 判定如果队列为空, 就进⾏ wait. (也是循环 wait)

  首先MyBlockingQueue 使用一个字符串数组 elems 来存储元素,同时维护了头指针 head、尾指针 tail 和队列中元素的个数 size。通过 synchronized 关键字和一个锁对象 locker,实现了线程安全的入队和出队操作。

  再用put 方法用于向队列中添加元素。如果队列已满,则调用 locker.wait() 阻塞当前线程,直到有空闲位置可供插入。然后将元素放入队列尾部,并更新尾指针和队列大小。最后调用 locker.notify() 唤醒其他可能正在等待的线程。

  take 方法用于从队列中取出元素。如果队列为空,则调用 locker.wait() 阻塞当前线程,直到有元素可供取出。然后取出头指针位置的元素,并更新头指针和队列大小。最后调用 locker.notify() 唤醒其他可能正在等待的线程,并返回取出的元素。

       最后在主函数中,创建了一个容量为 1000 的 MyBlockingQueue 对象,并依次向队列中放入了四个元素。然后使用 take 方法依次取出队列中的元素,并打印输出。

package 多线程;


//为了简单,不写作泛型的形式,考虑存储的元素就是单纯的String
    class MyBlockingQueue{

        private  String[] elems = null;

        private  int head = 0;

        private  int tail = 0;

        private  int size = 0;
        //准备一个锁对象
        private Object locker = new Object();


    public  MyBlockingQueue(int capacity){
        elems = new String[capacity];
    }

    public void put(String elem) throws InterruptedException {
        synchronized (locker){// 此处最好使⽤ while.
       // 否则 notifyAll 的时候, 该线程从 wait 中被唤醒,
       // 但是紧接着并未抢占到锁. 当锁被抢占的时候, 可能⼜已经队列满了
       // 就只能继续等待
            while (size >= elems.length){
                //队列满了
                locker.wait();
                //后续需要让这个代码能够阻塞;
                return;
            }
            //新的元素要放到tail 指向的位置上
            elems[tail] = elem;
            tail++;
            if(tail >= elems.length){
                tail = 0;
            }
            size++;

            locker.notify();//入队列成功后唤醒
        }
    }//写操作

    public String take() throws InterruptedException {
        String elem = null;
        synchronized (locker){
            while (size == 0){
                //队列空了
                locker.wait();
                // 后续也需要让这个代码阻塞

            }
            //取出 head 位置的元素并 返回
            elem = elems[head];
            head++;
            if (head >= elems.length){
                head = 0;

            }
            size--;
            locker.notify();//出队列成功后唤醒

        }
        return elem;
        }

}

public class ThreadDemo24 {
    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue queue = new MyBlockingQueue(1000);

        queue.put("aaa");
        queue.put("bbb");
        queue.put("ccc");
        queue.put("vvv");

        String elem = "";
        elem = queue.take();
        System.out.println("elem: " + elem);
        elem = queue.take();
        System.out.println("elem: " + elem);
        elem = queue.take();
        System.out.println("elem: " + elem);
        elem = queue.take();
        System.out.println("elem: " + elem);
    }
}

运行结果 

          希望大家多多支持!!! 

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

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

相关文章

769933-15-5,Biotin aniline,用来标记和检测细胞膜上的特定蛋白质

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;769933-15-5&#xff0c;Biotin aniline&#xff0c;生物素苯胺 一、基本信息 产品简介&#xff1a;Biotin aniline, also known as Biotin aniline, is a molecular probe with strong reactivity. Its uniqueness…

​(四)hive的搭建2

在&#xff08;三&#xff09;hive的搭建1中我们搭建好了hive环境&#xff0c;但是只能本地访问&#xff0c;在本节中配置Hive的访问方式。 1.元数据服务的方式 1.1 编辑hive-site.xml sudo vi hive-site.xml 在文件最后增加以下内容 <!– 指定存储元数据要连接的地址 –…

RTSP/Onvif协议视频平台EasyNVR激活码授权异常该如何解决

TSINGSEE青犀视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。在智慧安防等视频监控场景中&#xff0c;EasyNVR可提供视频实时监控直播、云端…

反物质(anti matter)和湮灭反应(Annihilation)浅读

反物质 反物质是正常物质的反状态。当正反物质相遇时&#xff0c;双方就会相互湮灭抵消&#xff0c;发生爆炸并产生巨大能量。 概念 正电子、负质子都是反粒子&#xff0c;它们跟通常所说的电子、质子相比较&#xff0c;电量相等但电性相反。科学家设想在宇宙中可能存在完全由…

自然语言nlp学习五

6-10 文本生成--介绍_哔哩哔哩_bilibili 在自然语言处理&#xff08;NLP, Natural Language Processing&#xff09;领域&#xff0c;“sequence”通常是指一个有序的数据集合&#xff0c;它由一系列元素按照特定顺序排列而成。这些元素可以是单词、字符、句子或其他文本单位。…

Web实战丨基于django+hitcount的网页计数器

文章目录 写在前面Django简介主要程序运行结果系列文章写在后面 写在前面 本期内容 基于djangohitcount的网页计数器 所需环境 pythonpycharm或vscodedjango 下载地址 https://download.csdn.net/download/m0_68111267/88795611 Django简介 Django 是一个开源的、基于 …

聊聊DoIP吧(一)

DoIP是啥? DoIP代表"Diagnostic over Internet Protocol",即互联网诊断协议。它是一种用于在车辆诊断中进行通信的网络协议。DoIP的目标是在现代汽车中实现高效的诊断和通信。通过使用互联网协议(IP)作为通信基础,DoIP使得诊断信息能够通过网络进行传输,从而提…

指针的学习1

目录 什么是指针&#xff1f; 野指针 造成野指针的原因&#xff1a; 如何避免野指针&#xff1f; 内存和指针 如何理解编址&#xff1f; 指针变量和地址 取地址操作符& 指针变量和解引用操作符 指针变量 如何拆解指针类型&#xff1f; 指针变量的大小 指针变量…

闲聊电脑(4)硬盘分区

夜深人静&#xff0c;万籁俱寂&#xff0c;老郭趴在电脑桌上打盹&#xff0c;桌子上的小黄鸭和桌子旁的冰箱又开始窃窃私语…… 小黄鸭&#xff1a;冰箱大哥&#xff0c;上次你说的那个“分区”和“格式化”是什么意思&#xff1f; 冰箱&#xff1a;分区么&#xff0c;就是分…

基于WordPress开发微信小程序1:搭建Wordpress

2年前&#xff0c;在知乎上提问&#xff1a;多数公司为什么宁愿自研也不用wordpress二次开发建站&#xff1f; - 知乎 (zhihu.com)&#xff0c;收到了&#xff0c;很多回答 自己打算做一下提升&#xff0c;便有了自己基于wordpress开发微信小程序的想法 项目定位 基于wordpre…

【机器学习】科学库使用手册第2篇:机器学习任务和工作流程(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论人工智能相关知识。主要内容包括&#xff0c;了解机器学习定义以及应用场景&#xff0c;掌握机器学习基础环境的安装和使用&#xff0c;掌握利用常用的科学计算库对数据进行展示、分析&#xff0c;学会使用jupyter note…

MATLAB知识点: 矩阵元素的修改和删除

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章 3.3.3 矩阵元素的修改和删除 我们可以直接利用等…

记录关于node接收上传文件formData踩的坑

1.vue2使用插件formidable实现接收文件&#xff0c;首先接口不可以使用任何中间件&#xff0c;否则form.parse()方法不执行。 const express require(express) const multipart require(connect-multiparty); const testController require(../controller/testController)/…

定义HarmonyOS IDL接口

HarmonyOS IDL简介 HarmonyOS Interface Definition Language&#xff08;简称HarmonyOS IDL&#xff09;是HarmonyOS的接口描述语言。HarmonyOS IDL与其他接口语言类似&#xff0c;通过HarmonyOS IDL定义客户端与服务端均认可的编程接口&#xff0c;可以实现在二者间的跨进程…

YOLOv5改进 | Neck篇 | 2024.1最新MFDS-DETR的HS-FPN改进特征融合层(轻量化Neck、全网独家首发)

一、本文介绍 本文给大家带来的改进机制是最近这几天最新发布的改进机制MFDS-DETR提出的一种HS-FPN结构,其是一种为白细胞检测设计的网络结构,主要用于解决白细胞数据集中的多尺度挑战。它的基本原理包括两个关键部分:特征选择模块和特征融合模块,在本文的下面均会有讲解,…

AJAX-认识URL

定义 概念&#xff1a;URL就是统一资源定位符&#xff0c;简称网址&#xff0c;用于访问网络上的资源 组成 协议 http协议&#xff1a;超文本传输协议&#xff0c;规定浏览器和服务器之间传输数据的格式&#xff1b;规定了浏览器发送及服务器返回内容的格式 协议范围&#xf…

flutter抓包绕过

lutter的证书校验 起因&#xff1a; 最近工作上让做个app的复测&#xff0c;把apk发我后&#xff0c;开始尝试挂代理抓包&#xff0c;结果发现抓不到 为是证书没弄好&#xff0c;想着前几天不是刚导入了吗&#xff08;雾&#xff09;。又重新导入了下还是不行。然后各种lsp模…

IDEA中的Run Dashboard

Run Dashboard是IntelliJ IDEA中的工具【也就是View中的Services】&#xff0c;提供一个可视化界面&#xff0c;用于管理控制应用程序的运行和调试过程。 在Run DashBoard中&#xff0c;可以看到所有的运行配置&#xff0c;以及每个配置的运行状态&#xff08;正在运行&#xf…

嵌入式—— IIC

目录 一、初识IIC 1. 介绍 2. 理解 二、IIC的简单拆分 1. 物理层 特点&#xff1a; 2. 协议层 &#xff08;1&#xff09;IIC基本读写过程 具体过程描述&#xff1a; &#xff08;2&#xff09;通信的起始和停止信号 &#xff08;3&#xff09;数据有效性 &#xf…

模拟请求ElasticSearch

Step1 安装chrome的这个插件 Step2 打开插件&#xff0c;GET的json填什么。 在IDEA的debug模式&#xff0c;走到Java代码的searchBuilder&#xff0c; 在这个searchBuilder变量里&#xff0c;对里面query变量点右侧 view按钮&#xff0c; IDEA里会显示出一个json&#xff…