当前位置:网站首页>不了解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^)/ ~ 「干货分享,每天更新」
边栏推荐
- 工件SSMwar exploded 部署工件时出错。请参阅服务器日志了解详细信息
- 如何重装Win11?一键重装Win11方法
- 1个月写900多条用例,二线城市年薪33W+的测试经理能有多卷?
- Flink Yarn Per Job - Yarn应用
- Excel导入和导出
- LeetCode_279_完全平方数
- The Spark of Sql join on the and and where
- [Three sons] C language implements simple three sons
- 在MySQL登录时出现Access denied for user ‘root‘@‘localhost‘ (using password YES) 拒绝访问问题解决
- Docker实践经验:Docker 上部署 mysql8 主从复制
猜你喜欢
随机推荐
Quartus uses tcl files to quickly configure pins
如何用Redis实现分布式锁?
@Transactional 注解使用详解
【加密周报】经济衰退在加息气氛中蔓延 美联储“放手一搏”?盘点上周加密市场发生的重大事件
Is TCP reliable?Why?
How to reinstall Win11?One-click method to reinstall Win11
2022还想上岸学习软件测试必看,测试老鸟的肺腑之言...
Excel文件读写(创建与解析)
在MySQL登录时出现Access denied for user ‘root‘@‘localhost‘ (using password YES) 拒绝访问问题解决
获取小猪民宿(短租)数据
mysql8安装make报错如何解决
security 会话并发管理
22.支持向量机—高斯核函数
月薪12K,蝶变向新,勇往直前—她通过转行测试实现月薪翻倍~
ICLR 2022 Best Paper: Partial Label Learning Based on Contrastive Disambiguation
[Three sons] C language implements simple three sons
Work for 5 years, test case design is bad?To look at the big case design summary
security cross-domain configuration
面试必问的HashCode技术内幕
async/await 原理及执行顺序分析









