多线程 (七) 阻塞队列的使用及其实现

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

一.什么是阻塞队列

二.使用阻塞队列/生产者消费者模型的好处 

 三.阻塞队列/生产者消费者模型的简单使用

 四. 模拟实现阻塞队列



一.什么是阻塞队列

阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空当队列满时,存储元素的线程会等待队列可用阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

1.当进行入队操作的时候,队列为满,入队操作就阻塞,直到队列非满的时候入队操作才完成

2.当进行出队操作的时候,队列为空,出队操作就阻塞,直到队列非空的时候出队操作才完成

生产者消费者模型的生活案例:

例如:两个人包饺子,其中有一个人需要生产饺子皮,他就是生产者,另外的一个人就是消费者,而生产者生产的饺子皮放在桌子上,桌子就是"交易场所".如果生产者生产过快饺子皮已经放满了桌子,他就能进行阻塞等待,如果是饺子皮的生产速度慢于包饺子的速度,那消费者就能够进行阻塞等待

二.使用阻塞队列/生产者消费者模型的好处 

       生产者消费者是一种高内聚,低耦合的模型,这也是它的优势,特别是在服务器场景中,假设有两个服务器A(请求服务器),B(应用服务器),如果A,B直接传递消息,而不通过阻塞队列,那么当A请求突然暴涨的时候,B服务器的请求也会跟着暴涨,由于B服务器是应用服务器,处理的任务是重量级的,所以该情况B服务器大概率会挂。

 但是,如果使用生产者消费者模型,那么即使A请求暴涨,也不会影响到B,顶多A挂了,应用服务器不会受到影响,这是因为A请求暴涨后,用户的请求都被打包到阻塞队列中(如果阻塞队列有界,则会引起队列阻塞,不会影响到B),B还是以相同的速度处理这些请求,所以生产者消费者模型可以起到“削峰填谷”的作用。

 三.阻塞队列/生产者消费者模型的简单使用

public class ThreadDemo1 {
    public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue =new LinkedBlockingDeque<>();
        //消费者
        Thread t1 =new Thread(() -> {
            while (true) {
                try {
                    int value =blockingQueue.take();
                    System.out.println("消费元素:"+value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        //生产者
        Thread t2 =new Thread(() -> {
            int value =0;
            while (true) {
                try {
                    System.out.println("生产元素:"+value);
                    blockingQueue.put(value);
                    value++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
    }
}

 四. 模拟实现阻塞队列

 1. 实现一个阻塞队列(这里采用顺序存储的环形队列)
 2. 保证线程安全
 3.  实现阻塞效果

class MyBlockingQueue {
    private  int[] items =new int[1000];
    private volatile int head =0;  //头部
    private volatile int tail =0;  //尾部
    private volatile int size =0;  //队列长度

    //入队列
    synchronized public void put(int elem) throws InterruptedException {
            //此外为什么if换成while更合适,防止别的代码暗中调用interrupt方法,把wait提前唤醒了
            //明明还不满足唤醒条件(队列没满才或者非空才会被唤醒) ,导致提前被唤醒就行执行下面代码,会出错抛异常
            while (size == items.length) {   //判断队列是否为满,满了则不能插入
                this.wait();                 //由take方法里的notify来唤醒
            }
            items[tail] = elem;   //进行插入操作,将elem放到items里,放到tail所指向的位置
            tail++;
            if (tail == items.length) {
                tail = 0;
            }
            size++;
            this.notify();
    }

    //出队列,返回删除的元素内容
    synchronized public int take() throws InterruptedException {
            while (size == 0) {  //判断队列是否为空,为空则不能出队
                this.wait();    //由put方法里的notify来唤醒
            }
            int value = items[head];    //非空,取元素
            head++;
            if (head == items.length) {
                head = 0;
            }
            size--;
            this.notify();
            return value;
        }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        MyBlockingQueue queue =new MyBlockingQueue();
        //消费者
        Thread t1 =new Thread(() -> {
            while (true) {
                //取元素
                try {
                    int value = queue.take();
                    System.out.println("消费:"+value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        //生产者
        Thread t2 =new Thread(() -> {
            int value =0;
            while (true) {
                try {
                    System.out.println("生产:" + value);
                    queue.put(value++);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
    }
}

 为了保证线程安全,需要注意的事项

1. 修改操作要保证原子性, put和take方法都有修改数据的操作, 所以这两个方法都直接加锁, 用 synchronized 修饰
2. 读操作要保证满足内存可见性, 所以 size, head和 tail都加上 volatile 修饰

为了满足阻塞队列的阻塞特性,需要在入队为满和出队为空时加wait方法进行阻塞等待,

而put 方法里判满时的 wait , 是由 take 方法最后的 notify 唤醒, take 里判空时的 wait , 是由 put方法最后的 notify 唤醒, put 和 take 不可能同时进入阻塞状态

wait方法是还有可能被外部的 interrupt 方法打断的,导致不是还没满足唤醒条件就继续执行下面代码,此时会出错抛异常,所以要把 if 换成 while , 如果不是被 notify 唤醒, 就再判断一下是否满足非空 / 非满这个条件

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

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

相关文章

科大奥瑞物理实验——交流电桥

实验名称&#xff1a;交流电桥 1. 实验目的&#xff1a; 掌握电桥平衡原理、平衡条件&#xff1b;理解交流电桥构造&#xff0c;熟悉交流电桥平衡方法&#xff1b;分析平衡过程中各桥臂调节顺序&#xff0c;判断最佳平衡点&#xff1b;理解复阻抗概念&#xff0c;电桥平衡时复…

虚拟机centos7配置Hadoop单节点伪分布配置教程

系列文章目录 centos7配置静态网络常见问题归纳_张小鱼༒的博客-CSDN博客 centos7克隆虚拟机完成后的的一些配置介绍_张小鱼༒的博客-CSDN博客 文章目录 目录 系列文章目录 文章目录 前言 一、前期准备 二、Hadoop介绍 2.1、Apache Hadoop 2.2、Cloudera Hadoop 2.3…

基于Vision Transformer的图像去雾算法研究与实现(附源码)

基于Vision Transformer的图像去雾算法研究与实现 0. 服务器性能简单监控 \LOG_USE_CPU_MEMORY\文件夹下的use_memory.py文件可以实时输出CPU使用率以及内存使用率&#xff0c;配合nvidia-smi监控GPU使用率 可以了解服务器性能是否足够&#xff1b;运行时在哪一步使用率突然…

第一个vue-cli项目

第一个vue-cli项目 12.1、什么是vue-cli vue-cli官方提供的一个脚手架&#xff0c;用于快速生成一个vue的项目模板&#xff1b;   预先定义好的目录结构及基础代码&#xff0c;就好比咱们在创建Maven项目时可以选择创建一个骨架项目&#xff0c;这个估计项目就是脚手架&…

编写一个函数,输入一个日期,计算其距年底的时间

--编写一个函数&#xff0c;输入一个日期&#xff0c;计算其距年底的时间 create or replace function f_end(i_date varchar2) return number is/*声明四个变量&#xff0c;v_end:存放输入的日期的年底日期 v_date:存放经过转化为日期型的输入字符串 v_minus:存放两个日期之差…

Springboot怎么实现WebSocket通信(二)

前言上一篇文章分享了单机模式下&#xff0c;websocket的基本使用方法&#xff0c;但在实际的业务中&#xff0c;通常是不会这样使用的&#xff0c;大部项目都是分布式部署的&#xff0c;一个工程布署了多个服务节点&#xff0c;前端并不直接请求具体服务节点&#xff0c;而是先…

xijs更新指南(v1.2.1)

xijs 是一款开箱即用的 js 业务工具库, 聚集于解决业务中遇到的常用函数逻辑问题, 帮助开发者更高效的开展业务开发.接下来就和大家一起分享一下v1.2.1 版本的更新内容以及后续的更新方向.1. 添加算法模块分类该模块主要由 WangLei802 贡献, 添加内容如下:添加冒泡排序算法及其…

什么是工程项目管理工作?其特点是什么?

什么是工程项目管理工作&#xff1f;其特点是什么&#xff1f; 工程项目管理是为了实现工程项目的有效、高效和可持续管理而进行的一系列活动。 工程项目的管理就像是驾驭一艘巨大的船只&#xff0c;需要一位经验丰富的船长来领导整个团队。 通过工程项目管理&#xff0c;项…

fiddler(抓包)的用法和HTTP 协议的基本格式

目录 fiddler(抓包)用法&#xff1a; HTTP 协议的基本格式 HTTP请求&#xff1a; 首行 认识HTTP方法 GET和POST的典型区别&#xff1a; 认识请求“报头”&#xff08;header&#xff09; HTTP 响应 HTTP状态码&#xff1a; 状态码的分类&#xff1a; 认识响应 …

python语法基础

&#x1f41f;在本次博客主要想大家介绍一些简单的python语法的注意事项&#xff0c;从代码缩进到注释规则&#xff0c;从标准输入到标准输出&#xff0c;以及位运算符等方面了解python的基础使用方法。那么我们接下来直接开始步入正题&#xff0c;开始我们的python语法的讲解吧…

【SpringCloud】SpringCloud Nacos详解(集群配置)

目录前言一.Nacos集群逻辑图二.Nacos集群搭建1.搭建数据库&#xff0c;初始化数据库表结构2.下载Nacos3.配置Nacos3.启动Nacos4.配置启动nginx5.测试是否成功6.设置服务的nacos地址7.新增一个配置&#xff0c;查看数据看是否进行持久化了前言 在我前面两篇讲的都是单个nacos&a…

c++11 多线程使用

文章目录创建线程异常导致死锁实现两个线程交互的打印奇数和偶数(面试题)创建线程 1.创建线程的方式: 1.拷贝构造禁止了2.允许移动构造3.无参构造后我们可以对对象进行赋值操作4.传递可调用对象(例如包装器,泛函数,lambda,普通函数,静态成员函数) 参数列表 进行创建 2.样例…

第17章_触发器

第17章_触发器 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公…

JavaEE简单示例——SpringMVC的简单数据绑定

简单介绍&#xff1a; 在前面我们介绍过如何将我们自己创建的类变成一个servlet来处理用户发送的请求&#xff0c;但是在大多数的时候&#xff0c;我们在请求 的时候会携带一些参数&#xff0c;而我们现在就开始介绍我们如何在Java类中获取我们前端请求中携带的参数。首先&…

SpringBoot自定义注解+异步来实现日志管理

一、前言 我们在企业级的开发中&#xff0c;必不可少的是对日志的记录&#xff0c;实现有很多种方式&#xff0c;常见的就是基于AOP注解进行保存&#xff0c;同时考虑到程序的流畅和效率&#xff0c;我们可以使用异步进行保存&#xff01; 二、基础环境 1. 导入依赖 我这里…

C#,码海拾贝(06)——连分式(Continued Fraction)曲线插值算法,《C#数值计算算法编程》源代码升级改进版

一、连分式法 连分式法是一种有理函数逼近法&#xff0c;其基本出发点是&#xff1a;将原型展开成连分式&#xff0c;然后截取前面几个起主要作用的偏系数构成简化模型&#xff0c;连分式法计算简便&#xff0c;拟合精度较高&#xff0c;是一种很有效的传递函数简化法。 Cont…

【Spring Cloud Alibaba】5.创建服务消费者(Feign)

文章目录简介什么是 Feign开始搭建创建项目修改POM文件添加启动类创建 Feign 接口添加Controller添加配置文件启动项目测试访问Nacos访问接口测试负载均衡通过终端启动多个服务提供者实例简介 接下来我们创建一个服务消费者&#xff0c;通过Feign来进行与服务提供者交互&#…

KDZD程控超低频高压发生器

一、产品概述 本产品接合了现代数字变频技术&#xff0c;采用微机控制&#xff0c;升压、降压、测量、保护自动化。由于电子化&#xff0c;所以体积小重量轻、大屏幕液晶显示&#xff0c;清晰直观、且能显示输出波形、打印试验报告。 设计指标符合《电力设备专用测试仪器通用…

SSM—【笔记】1.1Spring

Spring好处 简化开发&#xff0c;降低企业级开发的复杂性框架整合&#xff0c;高效整合其他技术&#xff0c;提高企业级应用开发与运行效率 简化开发&#xff1a;1、IoC、2、AOP[2.1衍生出事务处理 ] 框架整合&#xff1a;MyBatis、Mybatis-plus、Struts、Struts2、Hibernat…

Android开发-Android常用组件-ToggleButton开关按钮 Switch开关

4.7 开关按钮ToggleButton和开关Switch 1.开关按钮ToggleButton 属性名 说明 android:disabledAlpha 设置按钮在禁用时的透明度 android:textOff 按钮没有被选中时显示的文字 android:textOn 按钮被选中时显示的文字 另外&#xff0c;除了这个我们还可以自己写个 selec…