当前位置:网站首页>不了解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^)/ ~ 「干货分享,每天更新」
边栏推荐
猜你喜欢
With a monthly salary of 12K, the butterfly changed to a new one and moved forward bravely - she doubled her monthly salary through the career change test~
TCP 可靠吗?为什么?
Share an interface test project (very worth practicing)
2022第六届强网杯部分wp
security cross-domain configuration
How to reinstall Win11?One-click method to reinstall Win11
security CSRF漏洞保护
【Leetcode】1206. Design Skiplist
Win10安装DBeaver连接MySQL8、导入和导出数据库详细教程
Detailed explanation of Zadig's self-testing and tuning environment technical solution for developers
随机推荐
Flink Yarn Per Job - CliFrontend
WEB安全基础 - - - XRAY使用
【MySQL系列】MySQL索引事务
C language Qixi is coming!It's time to show the romance of programmers!
为什么要使用MQ消息中间件?这几个问题必须拿下
Zadig 面向开发者的自测联调子环境技术方案详解
Ansible中的任务执行控制
TCP 可靠吗?为什么?
SphereEx苗立尧:云原生架构下的Database Mesh研发实践
DOM 基础操作
Work for 5 years, test case design is bad?To look at the big case design summary
An interview question about iota in golang
分享一份接口测试项目(非常值得练手)
[头条]笔试题——最小栈
零基础如何学习单片机,一位入门者的进阶路径,可参考
学习笔记:机器学习之回归
security跨域配置
TexturePacker使用文档
Classical Literature Reading--DLO
C语言七夕来袭!是时候展现专属于程序员的浪漫了!