Juc04_阻塞队列概述、方法、实现类、Linked和Array区别、注意事项

news2024/12/30 4:30:42

文章目录

  • ①. 什么是阻塞队列
  • ②. BlockingQueue的主要方法
  • ③. BlockingQueue的实现类
  • ④. Linked和Array区别
  • ⑤. 不推荐使用快捷的线程池

①. 什么是阻塞队列

  • ①.阻塞队列:从名字可以看出,它也是队列的一种,那么它肯定是一个先进先出FIFO的数据结构。与普通队列不同的是,他支持两个附加操作,即阻塞添加和阻塞删除方法

  • ②. 线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素。而在这一系列操作必须符合以下规定:

  1. 阻塞添加:当阻塞队列是满时,往队列里添加元素的操作将被阻塞
  2. 阻塞移除:当阻塞队列是空时,从队列中获取元素/删除元素的操作将被阻塞

在这里插入图片描述

  • ③. 现有三个角色:顾客,休息区,银行办理窗口。Thread1为顾客,BlockingQueue为休息区,Thread2为银行办理窗口
  1. 正常情况下,一个银行办理窗口同一时间只能对接一个顾客
  2. 恰巧今天办理的顾客有3个人,另外2个顾客怎么办,你总不至于给人家说不办了,快回家吧
  3. 而正确的做法是你可以让这两个人在休息区等候,等银行窗口空闲了,然后去办理
  • ④. 阻塞队列的好处:阻塞队列不用手动控制什么时候该被阻塞,什么时候该被唤醒,简化了操作

②. BlockingQueue的主要方法

  • ①. BlockingQueue提供的部分方法:
public interface BlockingQueue<E> extends Queue<E> {
   
    boolean add(E e);

    boolean offer(E e);

    void put(E e) throws InterruptedException;

    boolean offer(E e, long timeout, TimeUnit unit)throws InterruptedException;
        
    E take() throws InterruptedException;

    E poll(long timeout, TimeUnit unit)throws InterruptedException;
        
    int remainingCapacity();

    boolean remove(Object o);

    public boolean contains(Object o);

    int drainTo(Collection<? super E> c);

    int drainTo(Collection<? super E> c, int maxElements);
}
  • ②. 根据插入和取出两种类型的操作,具体分为下面一些类型:
  1. 抛出异常是指当队列满时,再次插入会抛出异常如果队列未满,插入返回值未true
  2. 返回布尔是指当队列满时,再次插入会返回false
  3. 阻塞是指当队列满时,再次插入会被阻塞,直到队列取出一个元素,才能插入
  4. 超时是指当一个时限过后,才会插入或者取出

在这里插入图片描述

  • ③. 生产 - add、offer、put这3个方法都是往队列尾部添加元素,区别如下:
  1. add:不会阻塞,添加成功时返回true,不响应中断,当队列已满导致添加失败时抛出IllegalStateException
  2. offer:不会阻塞,添加成功时返回true,因队列已满导致添加失败时返回false,不响应中断
  3. put:会阻塞会响应中断
  • ④. 消费 - take、poll方法能获取队列头部第1个元素,区别如下:
  1. 会响应中断,会一直阻塞直到取得元素或当前线程中断
  2. 会响应中断,会阻塞,阻塞时间参照方法里参数timeout.timeUnit,当阻塞时间到了还没取得元素会返回null
  • ⑤. add方法代码以及结果
public class BlockingQueueTest {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);
        System.out.println("--------以下为add的相关操作---------");
        addRemoveTest(blockingQueue);
    }

    public static void addRemoveTest(BlockingQueue<String> blockingQueue) {
        System.out.println("添加状态+\t"+blockingQueue.add("1"));
        System.out.println("添加状态+\t"+blockingQueue.add("2"));
        System.out.println("添加状态+\t"+blockingQueue.add("3"));
//        System.out.println("添加状态+\t"+blockingQueue.add("4"));
        System.out.println("队首元素+\t"+blockingQueue.element());
        System.out.println("删除元素+\t"+blockingQueue.remove());
        System.out.println("队首元素+\t"+blockingQueue.element());
        System.out.println("删除元素+\t"+blockingQueue.remove());
        System.out.println("队首元素+\t"+blockingQueue.element());
        System.out.println("删除元素+\t"+blockingQueue.remove());
//        System.out.println("队首元素+\t"+blockingQueue.element());
//        System.out.println("删除元素+\t"+blockingQueue.remove(blockingQueue.element()));
    }
}
// 未打开注释代码输出如下:
--------以下为add的相关操作---------
添加状态+	true
添加状态+	true
添加状态+	true
队首元素+	1
删除元素+	1
队首元素+	2
删除元素+	2
队首元素+	3
删除元素+	3
// 当队列已满,继续添加元素时打开注释代码,输出如下:
--------以下为add的相关操作---------
添加状态+	true
添加状态+	true
添加状态+	true
Exception in thread "main" java.lang.IllegalStateException: Queue full
	at java.util.AbstractQueue.add(AbstractQueue.java:98)
  • ⑥. offer方法代码
public class BlockingQueueTest {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);
        System.out.println("--------以下为offer的相关操作---------");
       offerPollTest(blockingQueue);
    }

    private static void offerPollTest(BlockingQueue<String> blockingQueue) {
        System.out.println("添加状态+\t"+blockingQueue.offer("1"));
        System.out.println("添加状态+\t"+blockingQueue.offer("2"));
        System.out.println("添加状态+\t"+blockingQueue.offer("3"));
        System.out.println("添加状态+\t"+blockingQueue.offer("4"));
        System.out.println("队首元素+\t"+blockingQueue.peek());
        System.out.println("删除元素+\t"+blockingQueue.poll());
        System.out.println("队首元素+\t"+blockingQueue.peek());
        System.out.println("删除元素+\t"+blockingQueue.poll());
        System.out.println("队首元素+\t"+blockingQueue.peek());
        System.out.println("删除元素+\t"+blockingQueue.poll());
        System.out.println("删除元素+\t"+blockingQueue.poll());
    }
}
// 输出如下:
--------以下为offer的相关操作---------
添加状态+	true
添加状态+	true
添加状态+	true
添加状态+	false
队首元素+	1
删除元素+	1
队首元素+	2
删除元素+	2
队首元素+	3
删除元素+	3
删除元素+	null
// 注意:当队列没有元素的时候使用poll,返回null
  • ⑦. put方法代码
public class BlockingQueueTest {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(3);
        System.out.println("--------以下为put的相关操作---------");
        putTakeTest(blockingQueue);
    }

     private static void putTakeTest(BlockingQueue<String> blockingQueue) {
        try{
            blockingQueue.put("1");
            blockingQueue.put("2");
            blockingQueue.put("3");
//            blockingQueue.put("4");
            System.out.println("删除元素+\t"+blockingQueue.take());
            blockingQueue.put("4");
            System.out.println("删除元素+\t"+blockingQueue.take());
            System.out.println("删除元素+\t"+blockingQueue.take());
            System.out.println("删除元素+\t"+blockingQueue.take());
        }catch(Exception e){
            e.getStackTrace();
        }
    }
}
// 打开注释代码输出如下:(程序未停止)
--------以下为put的相关操作---------
// 未打开注释代码输出如下:
--------以下为put的相关操作---------
删除元素+	1
删除元素+	2
删除元素+	3
删除元素+	4

③. BlockingQueue的实现类

  • ①. 从整体架构图上来看,BlockingQueue是实现了Queue接口,而Queue是属于Collection接口下的派生类

在这里插入图片描述

  • ②. ArrayBlockingQueue: 由数组结构组成的有界阻塞队列

  • ③. LinkedBlockingQueue: 由链表结构组成的有界(但大小默认值Integer>MAX_VAL UE)阻塞队列
    需要注意的是LinkedBlockingQueue虽然是有界的,但有个巨坑,其默认大小是IntegerMAX_VALUE,高达21亿,一般情况下内存早爆了在线程池的ThreadPoolExecutor有体现

  • ④. SynchronousQueue:不存储元素的阻塞队列,也即是单个元素的队列

  1. SynchronousQueue没有容量,与其他BlcokingQueue不同,SynchronousQueue是一个不存储元素的BlcokingQueue
  2. 每个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然
public class SynchronusQueueTest {
    public static void main(String[] args) {
        BlockingQueue<String> synchronusQueue = new SynchronousQueue<>();

        new Thread(() ->{
            try{
                System.out.println(Thread.currentThread().getName()+"\t put 1");
                synchronusQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"\t put 2");
                synchronusQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"\t put 3");
                synchronusQueue.put("3");
            }catch(Exception e){
                e.getStackTrace();
            }
        },"Prod").start();

        new Thread(() ->{
            try {
                try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName()+"\t take "+synchronusQueue.take());
                try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName()+"\t take "+synchronusQueue.take());
                try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName()+"\t take"+synchronusQueue.take());
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"Cons").start();
    }
// 输出如下:有时间间隔
Prod	 put 1
Cons	 take 1
Prod	 put 2
Cons	 take 2
Prod	 put 3
Cons	 take3

  • ⑤. PriorityBlockingQueue:支持优先级排序的无界阻塞队列

  • ⑥. LinkedTransferQueue:由链表结构组成的无界阻塞队列

  • ⑦. LinkedBlockingDeque:由了解结构组成的双向阻塞队列

④. Linked和Array区别

  • ①. 队列大小不同:
  1. arrayBlockingQueue在初始化的时候,必须指定队列的大小
  2. 而LinkedBlockingQueue在初始化的时候,如果你没有指定大小,则会默认Integer.MAX_VALUE,是一个很大的值,这样就会导致数据在一个不可控范围,一旦添加速度远大于移除的速度时,可能会有内存泄漏的风险
  • ②. 底层实现不同
  1. arrayBlockingQueue的底层是一个数组
  2. LinkedBlockingQueue底层是一个链表结构
  3. 官方文档介绍中,LinkedBlockingQueue的吞吐行是高于arrayBlockingQueue;但是在添加或移除元素中,LinkedBlockingQueue则会生成一个额外的Node对象,对GC可能存在影响
  • ④. 为什么说LinkedBlockingQueue的吞吐性是高于arrayBlockingQueue?
    吞吐性能强是因为有两个锁,试想一下,Array里面使用的是一个锁,不管put还是take行为,都可能被这个锁卡住,而Linked里面put和take是两个锁,put只会被put行为卡住,而不会被take卡住,因此吞吐性能自然强于Array。 而“less predictable performance”这个也是显而易见的,Array采用的时固定内存,而Linked采用的时动态内存,无论是分配内存还是释放内存甚至GC动态内存的性能自然都会比固定内存要差

  • ⑤. 锁机制不一样
    arrayBlockingQueue使用的一个锁来控制,LinkedBlockingQueue使用了2个锁来控制,一个名为putLock,另一个是takeLock,但是锁的本质都是ReentrantLock

⑤. 不推荐使用快捷的线程池

  • ①. 我们需要根据自己的场景、并发情况来评估线程池的几个核心参数,包括核心线程数、最大线程数、线程回收策略、工作队列的类型,以及拒绝策略,确保线程池的工作行为符合需求,一般都需要设置有界的工作队列和可控的线程数

  • ②. 任何时候,都应该为自定义线程池指定有意义的名称,以方便排查问题。当出现线程数量暴增、线程死锁、线程占用大量 CPU、线程执行出现异常等问题时,我们往往会抓取线程栈。此时,有意义的线程名称,就可以方便我们定位问题

  • ③. newFixedThreadPool方法的源码不难发现,线程池的工作队列直接new了一个LinkedBlockingQueue,而默认构造方法的 LinkedBlockingQueue是一个Integer.MAX_VALUE 长度的队列,可以认为是无界的

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    ...


    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
...
}
  • ④. 翻看newCachedThreadPool的源码可以看到,这种线程池的最大线程数是Integer.MAX_VALUE,可以认为是没有上限的,而其工作队列 SynchronousQueue是一个没有存储空间的阻塞队列。这意味着,只要有请求到来,就必须找到一条工作线程来处理,如果当前没有空闲的线程就再创建一条新的
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
  • ⑤. 线程池的OOM问题,可能是队列满造成的(LinkedBlockingQueue),也可能是线程太多造成的(SynchronousQueue)

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

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

相关文章

Flask新手教程

Flask简介 Flask是一个轻量级的可定制框架&#xff0c;使用Python语言编写&#xff0c;较其他同类型框架更为灵活、轻便、安全且容易上手。 Flask 可以很好地结合MVC模式进行开发&#xff0c;开发人员分工合作&#xff0c;小型团队在短时间内就可以完成功能丰富的中小型网站或…

正点原子uboot分析

知识点 为终端不输出command line&#xff1a;终端输入如果变量quiet为空的话&#xff0c;整个命令都会输出。 如果变量 quiet为“ quiet_”的话&#xff0c;仅输出短版本。 如果变量 quiet为“ silent_”的话&#xff0c;整个命令都不会输出。sinclude&#xff1a;读取的文件…

Java通过JNI调用dll动态库详细步骤

目录 目标具体示例1、编写java代码&#xff1a;定义native接口2、根据java编写的native接口生成.h头文件3、使用Visual Studio编写c代码实现头文件接口并生成dll文件4、将生成的jni.dll文件放入jdk bin下5、编写java测试类&#xff0c;调用dll 附&#xff1a;问题java测试类执行…

上门家教app小程序源码开发的前景如何?

随着我国生活水平的提高&#xff0c;教育方面的问题也越来越受到家长们的重视&#xff0c;很多家庭都开始通过家教以及辅导班等方式增强学生的学业知识&#xff0c;因此家教app开发是拥有很大一部分用户市场的。那么家教app开发主要适合什么行业呢&#xff1f; 上门家教服务或…

Zabbix-客户端部署全过程

本文已收录于专栏 《中间件合集》 目录 概念说明什么是Zabbix 功能介绍配置过程1.在linux下的admin文件夹下创建zabbix文件夹2.把agent端压缩包放置到home/admin/zabbix路径下3.解压安装包4.创建zabbix日志文件5.进入到conf文件夹下&#xff0c;修改配置文件&#xff0c;与serv…

Linux系统下网络性能监控指令(iftop、nload)

文章目录 iftop参数快捷键iftop界面说明&#xff1a; nload参数示例 iftop 参数 -i 设定监测的网卡&#xff0c;如&#xff1a;# iftop -i eth1 -B 以bytes为单位显示流量(默认是bits)&#xff0c;如&#xff1a;# iftop -B -n 使host信息默认直接都显示IP&#xff0c;如&…

今天给大家安利几款非常好用的文件迁移工具

文件迁移是我们平时经常需要处理的问题之一。在日常生活中&#xff0c;我们可能需要将一些文件从电脑上的一个位置迁移到另一个位置&#xff0c;或者将文件转移到外部硬盘或云端存储空间。而为了更加高效地完成这项任务&#xff0c;使用一款好用的文件迁移工具显得尤为重要。今…

基于smardaten无代码开发解决光伏电站项目

文章目录 前言一、事故背景二、解决方案三、工具加持四、配置要点解析1、光伏导航菜单搭建2、运行监控组件布局3、员工填报表单创建4、数据接入清洗5、复杂数据展示5、地图大屏组装6、定制页面集成 五、挖掘更多惊喜1、模拟数据生成2、智能分析结果3、草图智能识别4、日期排班设…

el-dialog设置滚动条不生效记录【草稿版,待优化】

目录 前言一、scrollTop是什么&#xff1f;二、解决步骤总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 技术栈 element-plus vue3 js 记录el-dialog的弹窗中&#xff0c;通过js控制滚动条不生效。 我这次的需求是这样的&#xff0c;点击某个按钮…

SSM-Spring项目使用Tomcat:7插件运行项目注意事项

文章目录 配置Tomcat:7插件&#xff08;注意Path&#xff09;运行tomcat:7第一种方式第二种方式 测试路径&#xff08;注意&#xff09;正确请求方式&#xff08;注意路径&#xff09; 配置Tomcat:7插件&#xff08;注意Path&#xff09; 作为演示&#xff0c;我这里特别配置了…

中学生台灯怎么选比较好?精选真正适合中学生的台灯!

现在孩子的近视率很高&#xff0c;尤其是儿童青少年居多&#xff0c;从上了小学开始作业就变多了&#xff0c;经常挑起夜灯学习的&#xff0c;而中学生负担则更重。家长重视教育质量的同时也要注意孩子学习时的光线适合学习吗&#xff1f;用眼过度和不适合的光源容易导致近视&a…

Sangfor华东天勇战队:jeecg-boot登录分析

realkey获取 7evs1680077379806 可以看到代码逻辑如下&#xff0c;md5(小写验证码checkKey)&#xff0c;这里checkKey是时间戳 然后继续往下看 这里checkcode取值为空&#xff0c;这是为什么呢&#xff0c;是因为redisUtil中没有获得&#xff0c;登录的时候会存到redis中&a…

SLAM之反求运动和地图点(对极几何)

简介 前面的文章介绍了如何在已知空间点的情况下在不同坐标系中的表示&#xff08;刚体的坐标变换&#xff09;&#xff0c;以及如何将空间中的点投影到相机中生成图像&#xff0c;但是现实中的情况却是相反的情况&#xff08;空间点以及坐标系之间的变换未知&#xff09;&…

【裸机开发】定时器按键消抖(EPIT)

实际工程中&#xff0c;不能直接通过延时来消抖 ! 这里我们采用定时器来消抖&#xff0c;这也是内核处理消抖的一种方式。 目录 一、基本原理 1、延时消抖的弊端 2、定时器消抖原理 二、按键消抖实现 1、按键中断 2、定时器中断 三、附加&#xff1a;按键 / 定时器中断初…

前后端分离开发跨域问题总结

前后端分离开发跨域问题总结 一、什么是跨域访问二、解决跨域问题1、Vue前端配置代理解决跨域2、SpringBoot后端配置解决跨域2.1 跨域配置类CorsConfig&#xff08;常用&#xff09;2.2 Controller添加CrossOrigin注解2.3 添加CORS过滤器CorsFilter&#xff08;常用&#xff09…

selenium之鼠标操作

首先导入ActionChains类&#xff0c;该类可以完成鼠标移动&#xff0c;鼠标点击事件&#xff0c;键盘输入、内容菜单交互等交互行为。 from selenium.webdriver.common.action_chains import ActionChains 操作语法&#xff1a; 第一步&#xff1a;初始化ActionChains类&…

系列二、Maven下载安装配置

一、下载 链接&#xff1a;https://pan.baidu.com/s/1BvwLzAk9kRSP-daxSYe4Vw?pwdyyds 提取码&#xff1a;yyds 二、安装 第一步&#xff1a;下载安装包 第二步&#xff1a;解压至安装目录&#xff0c;例如 第三步&#xff1a;配置settings.xml&#xff08;主要配置maven本…

Open ai 开发指南:gpt接口的第一个问答机器人demo

目录 内容 Python代码 C 代码 workspace 文件 BUILD文件 Java 代码 maven文件 执行效果 内容 基于openai接口实现循环gpt问答&#xff0c;并使用一个文件将问答内容进行记录。 Python代码 # -*- coding: utf-8 -*- import openai import time from pathlib import P…

100天精通Python(可视化篇)——第93天:Pyecharts绘制多种炫酷饼图参数说明+代码实战(百分比、环形、玫瑰、内嵌、多个子图饼图)

文章目录 专栏导读1. 基础饼图add函数简单案例改变颜色 2. 百分比饼图3. 环形饼图4. 玫瑰饼图5. 内嵌环图6. 多个饼图 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#xff1a;本专栏专门针对零基础和需要进阶提升的同学所准备的一套完整…

学习前端开发,能抛弃HTML和CSS吗?

前言 前端开发里面HTML和CSS是两个非常重要的核心技术&#xff0c;它们是构建网页和应用程序界面的基础。 HTML&#xff08;超文本标记语言&#xff09;是用于描述网页结构的标记语言&#xff0c;它定义了网页的内容、布局和元素。几乎所有的网页都使用HTML来组织和呈现内容&…