当前位置: 首页 > news >正文

【JUC并发编程】ArrayBlockingQueue和LinkedBlockingQueue源码2分钟看完

文章目录

  • 1、BlockingQueue
    • 1)接口方法
    • 2)阻塞队列分类
  • 2、ArrayBlockingQueue
    • 1)构造函数
    • 2)put()入队
    • 3)take()出队
  • 3、LinkedBlockingQueue
    • 1)构造函数
    • 2)put()入队
    • 3)take()出队

1、BlockingQueue

BlockingQueue是JUC包下提供的一个阻塞队列 接口;

1)接口方法

在这里插入图片描述

队列操作

  • 抛出异常:add(e)、remove()、element()
  • 返回特定值:offer()队尾入队/poll()删除队头元素/peek()
  • 一直阻塞:put(e)/take()
  • 超时退出:offer(e,time,unit)/poll(time,unit)

其中:BlockingQueue 不接受 null 元素。试图 add 、 put 或 offer ⼀个 null 元素时,某些实现会抛出 NullPointerException 。

在这里插入图片描述

2)阻塞队列分类

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

  • LinkedBlockingQueue:由链表组成的有界阻塞队列。

  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列(默认升序排序)。

  • DelayQueue:支持延时获取元素的无界阻塞队列。

    队列使用PriorityQueue来实现。

  • SynchronousQueue:一个不存储元素的阻塞队列。

  • LinkedTransferQueue:由链表组成的无界阻塞队列。

    其多了tryTransfer()方法和transfer()方法。

    • transfer():可以把生产者传入的元素立刻传输给消费者。如果没有consumer在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
    • tryTransfer():用来试探生产者传入的元素是否能直接传给消费者。不管是否有consumer正在等待接收元素,都立刻返回。

2、ArrayBlockingQueue

ArrayBlockingQueue是由数组结构组成的有界阻塞队列。通过ReentrantLock保证线程安全、并实现 Producer-Consumer模式。

1)构造函数

在这里插入图片描述

从构造函数可知,默认采用非公平锁;

public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();// 底层使用对象数组保存元素this.items = new Object[capacity];// 初始化需要加锁使用的ReentrantLock实例,默认采用非公平锁lock = new ReentrantLock(fair);/*** 判断队列是 空 or 满*     notEmpty⽤于执⾏take时进⾏await()等待操作,put时进⾏signal()唤醒操作*     notFull⽤于执⾏take时进⾏signal()唤醒操作,put时进⾏await()等待操作*/notEmpty = lock.newCondition();notFull =  lock.newCondition();
}

方法释义:

  • 构造函数的入参capacity指定了底层存储元素数组⻓度的⼤⼩;
  • 初始化需要加锁使⽤的ReentrantLock实例,默认采⽤的是⾮公平锁;
  • 基于Lock的Condition判断队列是 空 or 满
    • notEmpty⽤于执⾏take时进⾏await()等待操作,put时进⾏signal()唤醒操作;
    • notFull⽤于执⾏take时进⾏signal()唤醒操作,put时进⾏await()等待操作;

2)put()入队

public void put(E e) throws InterruptedException {// 入参不能为空checkNotNull(e);final ReentrantLock lock = this.lock;// 加可中断锁lock.lockInterruptibly();try {while (count == items.length)// 队列满了,则阻塞等待signal唤醒,同时释放次有的锁。notFull.await();// 入队操作enqueue(e);} finally {lock.unlock();}
}private void enqueue(E x) {final Object[] items = this.items;items[putIndex] = x;// 如果数据已经插入到数组末尾,则重置putIndex为0,从0开始继续插入。if (++putIndex == items.length)putIndex = 0;count++;// 通知take线程解除阻塞notEmpty.signal();
}

方法释义:

  • ⾸先尝试获得可中断锁,即:lock.lockInterruptibly(),当执⾏interrupt操作时,该锁可以被中断。
  • 如果数组中元素的个数(count)等于数组的⻓度了,说明队列已经满了,在该线程上执⾏等待操作:notFull.await(); ,等待signal唤醒。
  • 如果队列没有满,则调⽤enqueue(e)⽅法执⾏⼊列操作;
    1. ⼊列操作⾸先会将待插⼊值x放⼊数组下标为putIndex的位置上,然后再将putIndex加1,来指向下⼀次插⼊的下标位置。
      • 如果加1后的putIndex等于了数组的⻓度,那么说明已经越界了(因为putIndex是从0开始的);做循环式插⼊,重置putIndex为0,从0开始继续插入。
  • 最后,执⾏count++来计算元素总个数;并调⽤notEmpty.signal()⽅法来解除阻塞;
    • 当队列为空的时候,执⾏take⽅法会被notEmpty.await()阻塞;
    • 此处就是通知take线程解除阻塞

3)take()出队

public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)// 如果队列为空,则线程阻塞 等待signal唤醒,释放持有的锁notEmpty.await();// 执行出队操作return dequeue();} finally {lock.unlock();}
}private E dequeue() {final Object[] items = this.items;@SuppressWarnings("unchecked")// 获取takeIndex下标的元素E x = (E) items[takeIndex];// 将takeIndex下标下的袁术置为null,便于后面GC回收items[takeIndex] = null;// 如果出队操作的到了数组末尾,则重置takeIndex,从0开始继续取出if (++takeIndex == items.length)takeIndex = 0;count--;// 默认itrs为null,不会走进if代码段。if (itrs != null)itrs.elementDequeued();// 通知put线程解除阻塞notFull.signal();return x;
}

方法释义:

  • take⽅法和put⽅法类似,区别是出队的指针是takeIndex;

  • ⾸先尝试获得可中断锁,即:lock.lockInterruptibly(),当执⾏interrupt操作时,该锁可以被中断。

  • 如果队列中为空;执⾏notEmpty.await()将线程阻塞 等待signal唤醒,释放持有的锁。

    • 当调⽤put⽅法向队列中放⼊元素之后 ,会调⽤notEmpty.signal⽅法对等待的线程执⾏唤醒操作;
  • 如果出队操作的到了数组末尾,则重置takeIndex,从0开始继续取出;

  • 出队执⾏完毕后,调⽤notFull.signal⽅法来唤醒在notFull上⾯

    await的线程。 通知put线程解除阻塞。

3、LinkedBlockingQueue

LinkedBlockingQueue是由链表结构组成的有界阻塞队列。通过ReentrantLock保证线程安全、并实现 Producer-Consumer模式。

1)构造函数

如果不指定容量,则默认LinkedBlockingQueue是无界阻塞队列(capacity = Integer.MAX_VALUE)

在这里插入图片描述

构造函数中会创建⼀个空的节点,作为整个链表的头节点。

2)put()入队

在这里插入图片描述

private void enqueue(Node<E> node) {// assert putLock.isHeldByCurrentThread();// assert last.next == null;last = last.next = node;
}

整个put()流程和ArrayBlockingQueue基本一致,对于链表容量的统计会额外采用一个AtomiceInteger类型的变量count维护。

    • 最后唤醒put()线程的代码段上有一个 c == 0的判断,这里的c是入队操作之前的数量。
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();

这里有个比较有意思的点:

  • 入队操作添加完元素之后,如果发现当前队列的元素数量还没到最大容量,则尝试唤醒其他put()操作阻塞的线程;
if (c + 1 < capacity)notFull.signal();

3)take()出队

在这里插入图片描述

private E dequeue() {// assert takeLock.isHeldByCurrentThread();// assert head.item == null;Node<E> h = head;Node<E> first = h.next;h.next = h; // help GChead = first;E x = first.item;first.item = null;return x;
}

整个take()流程和ArrayBlockingQueue基本一致,稍微看一下即可。

  • 最后唤醒take()线程的代码段上有一个 c == capacity的判断,这里的c是出队操作之前的数量。

和LinkedBlockingQueue的put()操作一样:

  • 出队操作移除完元素之后,如果发现当前队列的元素数量 > 1,则尝试唤醒其他take()操作阻塞的线程;
if (c > 1)notEmpty.signal();

相关文章:

【JUC并发编程】ArrayBlockingQueue和LinkedBlockingQueue源码2分钟看完

文章目录1、BlockingQueue1&#xff09;接口方法2&#xff09;阻塞队列分类2、ArrayBlockingQueue1&#xff09;构造函数2&#xff09;put()入队3&#xff09;take()出队3、LinkedBlockingQueue1&#xff09;构造函数2&#xff09;put()入队3&#xff09;take()出队1、Blocking…...

GitHub个人资料自述与管理主题设置

目录 关于您的个人资料自述文件 先决条件 添加个人资料自述文件 删除个人资料自述文件 管理主题设置 补充&#xff1a;建立一个空白文件夹 关于您的个人资料自述文件 可以通过创建个人资料 README&#xff0c;在 GitHub.com 上与社区分享有关你自己的信息。 GitHub 在个…...

Express篇-连接mysql

创建数据库配置文件config/sqlconfig.jsconst sqlconfig {host: localhost, // 连接地址user: root, //用户名password: ****, //密码port: 3306 , //端口号database: mysql01_dbbooks //数据库名 } module.exports sqlconfig封装数据库管理工具 utils/mysqlUtils.…...

win10 安装rabbitMQ详细步骤

win10 安装rabbitMQ详细步骤 win10 安装rabbitMQ详细步骤win10 安装rabbitMQ详细步骤一、下载安装程序二、安装配置erlang三、安装rabbitMQ四、验证初始可以通过用户名&#xff1a;guest 密码guest来登录。报错&#xff1a;安装RabbitMQ出现Plugin configuration unchanged.问题…...

【成为架构师课程系列】一线架构师:6个经典困惑及其解法

目录 一线架构师:6个经典困惑及其解法 多阶段还是多视图&#xff1f; 内置最佳实践 架构方法论:3个阶段&#xff0c;一个贯穿 Pre-architecture阶段&#xff1a;ADMEMS矩阵方法 Conceptual Architecture阶段&#xff1a;重大需求塑造做概念架构 Refined Architecture…...

光耦合器的定义与概述

光耦合器或光电耦合器是一种电子元件&#xff0c;基本上充当具有不同电压电平的两个独立电路之间的接口。光耦合器是可在输入和输出源之间提供电气隔离的常用元件。它是一个 6 引脚器件&#xff0c;可以有任意数量的光电探测器。 在这里&#xff0c;光源发出的光束作为输入和输…...

谷粒商城--品牌管理详情

目录 1.简单上传测试 2.Aliyun Spring Boot OSS 3.模块mall-third-service 4.前端 5.数据校验 6.JSR303数据校验 7.分组校验功能 8.自定义校验功能 9.完善代码 1.简单上传测试 OSS是对象存储服务&#xff0c;有什么用呢&#xff1f;把图片存储到云服务器上能让所有人…...

stack、queue和priority_queue

目录 一、栈&#xff08;stack&#xff09; 1.stack的使用 2.容器适配器 3.stack的模拟实现 二、队列&#xff08;queue&#xff09; 1.queue的使用 2.queue的模拟实现 三、双端队列&#xff08;deque&#xff09; 1.vector&#xff0c;list的优缺点 2.认识deque 四…...

面试题(二十二)消息队列与搜索引擎

2. 消息队列 2.1 MQ有什么用&#xff1f; 参考答案 消息队列有很多使用场景&#xff0c;比较常见的有3个&#xff1a;解耦、异步、削峰。 解耦&#xff1a;传统的软件开发模式&#xff0c;各个模块之间相互调用&#xff0c;数据共享&#xff0c;每个模块都要时刻关注其他模…...

Spring Security in Action 第三章 SpringSecurity管理用户

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;以实战为线索&#xff0c;逐步深入SpringSecurity相关知识相关知识&#xff0c;打造完整的SpringSecurity学习步骤&#xff0c;提升工程化编码能力和思维能力&#xff0c;写出高质量代码。希望大家都能够从中有所收获&#…...

Java面试——maven篇

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

基于微信小程序的游戏账号交易小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…...

Matlab绘制隐函数总结-二维和三维

1.二维隐函数 二维隐函数满足f(x,y)0f(x,y)0f(x,y)0&#xff0c;这里无法得到yf(x)yf(x)yf(x)的形式。不能通过普通函数绘制。 我们要关注的是使用fplot函数和fimplicit函数。 第1种情况&#xff1a;基本隐函数 基本的隐函数形式形如&#xff1a; x2y22x2(x2y2)12x^{2}y^{…...

如何直观地理解傅立叶变换?频域和时域的理解

如何直观地理解傅立叶变换 傅里叶变换连续形式的傅立叶变换如何直观地理解傅立叶变换?一、傅里叶级数1.1傅里叶级数的三角形式1.2 傅里叶级数的复指数形式二、傅里叶变换2.1一维连续傅里叶变换三、频谱和功率谱3.1频谱的获得3.2频谱图的特征3.3频谱图的组成频域(frequency do…...

STC15读取内部ID示例程序

STC15读取内部ID示例程序&#x1f389;本案例基于STC15F2K60S2为验证对象。 &#x1f4d1;STC15 ID序列介绍 STC15系列STC最新一代STC15系列单片机出厂时都具有全球唯一身份证号码(ID号)。最新STC15系列单片机的程序存储器的最后7个字节单元的值是全球唯一ID号&#xff0c;用…...

Xml格式化与高亮显示

具体请参考&#xff1a;Xml格式化与高亮显示...

【GlobalMapper精品教程】045:空间分析工具(2)——相交

GlobalMapper提供的空间分析(操作)的方法有:交集、并集、单并集、差异、对称差集、相交、重叠、接触、包含、等于、内部、分离等,本文主要讲述相交工具的使用。 文章目录 一、实验数据二、符号化设置三、相交运算四、结果展示五、心灵感悟一、实验数据 加载配套实验数据(…...

4年外包终上岸,我只能说这类公司能不去就不去..

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司&#xff0c;一干就是4年。现在终于跳槽到了互联网公司了&#xff0c;我想说的是&#xff0c;但凡有点机会&#xff0c;千万…...

sklearn降维算法1 - 降维思想与PCA实现

目录1、概述1.1 维度概念2、PCA与SVD2.1 降维实现2.2 重要参数n_components2.2.1 案例&#xff1a;高维数据的可视化2.2.2 最大似然估计自选超参数2.2.3 按信息量占比选超参数1、概述 1.1 维度概念 shape返回的结果&#xff0c;几维几个方括号嵌套 特征矩阵特指二维的 一般来…...

「期末复习」线性代数

第一章 行列式 行列式是一个数&#xff0c;是一个结果三阶行列式的计算&#xff1a;主对角线的乘积全排列与对换逆序数为奇就为奇排列&#xff0c;逆序数为偶就为偶排列对换&#xff1a;定理一&#xff1a;一个排列的任意两个元素对换&#xff0c;排列改变奇偶性&#xff08;和…...

伏并网低电压穿越技术

国内光伏并网低电压穿越要求 略&#xff1a; 低电压穿越方法 当前&#xff0c;光伏电站实现低电压穿越可通过两种方式&#xff0c;即增加硬件设备或者改变控制策略。本节对基于储能设备、基于无功补偿设备、基于无功电流电压支撑控制策略三种实现LVRT的典型方法进行介绍。 …...

opencv的环境搭建

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…...

C++智能指针

c11的三个智能指针 unique_ptr独占指针&#xff0c;用的最多 shared_ptr记数指针&#xff0c;其次 weak_ptr&#xff0c;shared_ptr的补充&#xff0c;很少用 引用他们要加上头文件#include unique_ptr独占指针&#xff1a; 1.只能有一个智能指针管理内存 2.当指针超出作用域…...

MongoDB--》MongoDB数据库以及可视化工具的安装与使用—保姆级教程

目录 数据库简介 MongoDB数据库的安装 MongoDB数据库的启动 MongoDB数据库环境变量的配置 MongoDB图形化管理工具 数据库简介 在使用MongoDB数据库之前&#xff0c;我们应该要知道我们使用它的原因&#xff1a; 在数据库当中&#xff0c;有常见的三高需求&#xff1a; Hi…...

JAVA 基础题

1. 面向对象有哪些特征&#xff1f;答&#xff1a;继承、封装、多态2. JDK与JRE的区别是什么&#xff1f;答&#xff1a;JDK是java开发时所需环境&#xff0c;它包含了Java开发时需要用到的API&#xff0c;JRE是Java的运行时环境&#xff0c;JDK包含了JRE&#xff0c;他们是包含…...

Flutter desktop端多屏幕展示问题处理

目前越来越多的人用Flutter来做桌面程序的开发&#xff0c;很多应用场景在Flutter开发端还不是很成熟&#xff0c;有些场景目前还没有很好的插件来支持&#xff0c;所以落地Flutter桌面版还是要慎重。 下面来说一下近期我遇到的一个问题&#xff0c;之前遇到一个需要双屏展示的…...

每天10个前端小知识 【Day 9】

&#x1f469; 个人主页&#xff1a;不爱吃糖的程序媛 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、CSDN内容合伙人&#xff0c;专注于前端各领域技术&#xff0c;成长的路上共同学习共同进步&#xff0c;一起加油呀&#xff01; ✨系列专栏&#xff1a;前端…...

Elasticsearch的读写搜索过程

问题 Elasticsearch在读写数据的过程是什么样的?你该如何理解这个问题&#xff01; Elasticsearch的写数据过程 客户端选择一个节点发送请求&#xff0c;这个时候我们所说的这个节点就是协调节点&#xff08;coordinating node&#xff09;协调节点对document进行了路由&am…...

线上服务质量的问题该如何去处理?你有什么思路?

线上服务质量的问题该如何去处理&#xff1f;你有什么思路&#xff1f; 目录&#xff1a;导读 发现线上故障 处理线上故障 修复线上故障 运营线上质量 就是前几天有个同学问了我一个问题&#xff1a;目前业内高可用部署主要采用方案&#xff1f; 看到这个问题&#xff0c…...

IOC 配置,依赖注入的三种方式

xml 配置 顾名思义&#xff0c;就是将bean的信息配置.xml文件里&#xff0c;通过Spring加载文件为我们创建bean。这种方式出现很多早前的SSM项目中&#xff0c;将第三方类库或者一些配置工具类都以这种方式进行配置&#xff0c;主要原因是由于第三方类不支持Spring注解。 优点…...

南通门户网站建设/超级外链工具有用吗

NOTE: 1.APIs往往要求访问原始资源&#xff08;raw resources&#xff09;,所以每一个RAII class应该提供一个“取得其所管理之资源”的办法。 2.对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全&#xff0c;但隐式转换对客户比较方便。 转载于:https:/…...

做网站banner图必备的/网店营销策划方案ppt

传送门&#xff1a;然而并没有... 这两天测试状态比较奇怪&#xff0c;前面1.5h-2h完全不知所措&#xff0c;一脸茫然&#xff0c;感觉自己吃枣药丸 后面才发现有很多题可捉的&#xff0c;并没有想象的那么难&#xff0c;但是时间已经不充裕了... R2D2 T1&#xff1a;不知所措数…...

wordpress 动态特效/产品宣传

1 ICMP Internet 控制消息2 IGMP Internet 组管理3 GGP 网关对网关4 IP IP 中的 IP&#xff08;封装&#xff09; 5 ST 流6 TCP 传输控制8 EGP 外部网关协议9 IGP 任何专用内部网关&#xff08;Cisco 将其用于 IGRP&#xff09;17 UDP 用户数据报18 MUX 多路复用 29 ISO-TP4 IS…...

台州路桥网站建设/常用的seo工具

之前遇到过"/",在esclipse中报错&#xff0c;只认识“//”&#xff0c;“\”符号&#xff0c;需要将string字符串“/”&#xff0c;转换成“//”或者“\”怎么转呢&#xff1f; 获取字符串是 String path "/root/data/image"; StringBuilder sb new Stri…...

如何在百度做自己公司的网站/优化网站关键词优化

网络请求urllib库使用总结 目录网络请求urllib库使用总结1.概述2.Urllib库基本使用2.1.Urllib库快速上手1.完成一次简单的get请求2.read()函数介绍3.获取其他响应信息2.2.urlretrieve()函数下载数据1.urlretrieve()函数基本使用2.3.构建请求对象1.请求一个https网站2.攻破UA反爬…...

做旅游网站会遇到什么问题/什么是网络推广营销

Python相比于C语言、PHP、Java等编程语言&#xff0c;更加简单易学&#xff0c;很多没有编程经验或者对编程一知半解的人员往往会选择Python作为入门编程语言&#xff0c;Python虽然入门简单&#xff0c;但是要想更深入的掌握Python知识和技能&#xff0c;还需要下一番苦功&…...