当前位置:网站首页>不了解SynchronousQueue?那ArrayBlockingQueue和LinkedBlockingQueue不会也不知道吧?
不了解SynchronousQueue?那ArrayBlockingQueue和LinkedBlockingQueue不会也不知道吧?
2022-08-02 00:00:00 【爪哇缪斯】
一、BlockingQueue
在我们分析线程池的时候,就涉及过阻塞队列。通过它的特性,我们可以从阻塞队列中逐一的获取需要的元素。从它的名字上就可以看得出,阻塞队列本质上依然是队列结构,即:满足先进先出的数据结构特性。同时,阻塞队列也根据调用方法后的不同响应形式,提供了多种存/取元素的方法,具体区别如下表所示:

由于使用offer方法时,如果队列已经满了,那么则无法插入成功,会立即返回false;同样的,当我们调用poll方法的时候,如果队列中是空的,则也会立即返回false;而比起立即返回的情况,我们更关注于put和take这种插入或移除失败,在当前阻塞的情况是如何实现的。之前我们曾经解析过阻塞队列SynchronousQueue——多图详解阻塞队列——SynchronousQueue,那么本篇文章我们就以ArrayBlockingQueue和LinkedBlockingQueue为例,看看内部是怎么实现的。
二、ArrayBlockingQueue
在构造函数中,默认采用非公平锁方式构造阻塞队列。由构造函数入参capacity来指定底层存储元素数组的长度。并且初始化了需要加锁使用的ReentrantLock实例,默认采用的是非公平锁。其中,notEmpty用于执行take时进行await()等待操作,以及put时进行signal()唤醒操作。notFull用于执行take时进行signal()唤醒操作,及put时进行await()等待操作。所以,其实就是用于标识队列是否为空或者标识队列是否满了的这两种特殊情况。相关源码及注释如下图所示:

put方法逻辑解析
在执行put方法逻辑之前,首先尝试获得可中断锁——即:lock.lockInterruptibly(),当执行interrupt操作时,该锁可以被中断。
如果数组中元素的个数(count)等于数组的长度了,也就说明队列满了,那么就在该线程上执行等待操作——notFull.await();
如果队列没有满,则调用enqueue(e)方法执行入列操作。入列操作首先会将待插入值x放入数组下标为putIndex的位置上,然后再将putIndex加1,来指向下一次插入的下标位置。此处需要注意的是,如果加1后的putIndex等于了数组的长度,那么说明已经越界了(因为putIndex是从0开始的),那么此时将putIndex置为0,即:待插入的指针指向了数组的头部——循环式插入。
最后,执行count++来计算元素总个数,并且调用notEmpty.signal()方法来解除阻塞(即:当队列为空的时候,执行take方法会被notEmpty.await()阻塞)。相关源码及注释如下图所示:

take方法逻辑解析
take方法跟上面我们分析的put方法类似,区别是:出队的指针是takeIndex。如果队列中为空,那么当调用take方法执行出队操作时,就会执行notEmpty.await()方法执行等待操作,并释放锁资源。
当调用put方法向队列中放入元素之后 ,会调用notEmpty.signal()方法对等待的线程执行唤醒操作。那么线程继续执行出队操作,执行完毕后,会调用notFull.signal方法来唤醒在notFull上面await的线程。相关源码及注释如下图所示:

三、LinkedBlockingQueue
构造函数,默认长度为2^31,大概21亿多。在构造函数中,创建一个空的节点,作为整个链表的头节点。相关源码及注释如下图所示:

put源码和注释如下所示:
LinkedBlockingQueue的put方法和take方法逻辑都比较简单,与ArrayBlockingQueue的处理方式也很类似,区别就在于用于底层实现的用于存储元素的数据结构是不同的。这里使用的是链表结构存储的元素。 通样的,在调用put方法时,依然通过lockInterruptibly()对插入操作进行加锁操作。如果当时队列中已经满了,即:队列中存储的元素数量已经达到了Integer.MAX_VALUE,那么则执行notFull.await()进行等待,直到队列达到未满状态,执行notFull.signal()方法解除等待状态。
插入时,调用的是enqueue(...)方法,在这个方法中,将新添加的元素放入到链表的队尾,就完成了入队的操作。相关源码及注释如下图所示:

take源码和注释如下所示:
take方法与put方法也很类似,都是先调用lockInterruptibly()进行加锁操作。不同点是,这里由于是获取元素的操作,所以更在意的是队列是否为空,那么此时我们采用的就是notEmpty.await()和notEmpty.signal()来控制阻塞和解除阻塞。
获取元素时,调用的是dequeue(...)方法,在这个方法中,是将head指针指向的元素指向出队操作。然后将head头指针指向原head头指针元素的next节点即可。相关源码及注释如下图所示:

今天的文章内容就这些了:
写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。
更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」
边栏推荐
- ICLR 2022 Best Paper: Partial Label Learning Based on Contrastive Disambiguation
- 22.支持向量机—高斯核函数
- 单片机遥控开关系统设计(结构原理、电路、程序)
- CDH6的Hue打开出现‘ascii‘ codec can‘t encode characters
- 工件SSMwar exploded 部署工件时出错。请参阅服务器日志了解详细信息
- yay 报错 response decoding failed: invalid character ‘<‘ looking for beginning of value;
- 很多人喜欢用多御安全浏览器,竟是因为这些原因
- 学习笔记:机器学习之回归
- 【MySQL系列】MySQL索引事务
- oozie startup error on cdh's hue, Cannot allocate containers as requested resource is greater than maximum allowed
猜你喜欢
随机推荐
一个有些意思的项目--文件夹对比工具(一)
security跨域配置
Docker搭建Mysql主从复制
接地气讲解TCP协议和网络程序设计
CDH6 Hue to open a "ASCII" codec can 't encode characters
【无标题】
A brief analysis of mobile APP security testing in software testing, shared by a third-party software testing agency in Beijing
【Leetcode】1206. Design Skiplist
Work for 5 years, test case design is bad?To look at the big case design summary
利用“栈”快速计算——逆波兰表达式
OpenCV DNN blogFromImage()详解
async和await用法介绍
Several interview questions about golang concurrency
Architecture basic concept and nature of architecture
Flink Yarn Per Job - CliFrontend
22.支持向量机—高斯核函数
Win10安装DBeaver连接MySQL8、导入和导出数据库详细教程
An interview question about iota in golang
Docker实践经验:Docker 上部署 mysql8 主从复制
一道golang中关于iota的面试题









