当前位置:网站首页>不了解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^)/ ~ 「干货分享,每天更新」
边栏推荐
- 【Leetcode】479. Largest Palindrome Product
- 递归:方法调用自身
- 在MySQL登录时出现Access denied for user ‘root‘@‘localhost‘ (using password YES) 拒绝访问问题解决
- security CSRF漏洞保护
- 一篇永久摆脱Mysql时区错误问题,idea数据库可视化插件配置
- 一款简洁的文件传输工具
- 工作5年,测试用例都设计不好?来看看大厂的用例设计总结
- Several interview questions about golang concurrency
- 【Leetcode】478. Generate Random Point in a Circle(配数学证明)
- 根本上解决mysql启动失败问题Job for mysqld.service failed because the control process exited with error code
猜你喜欢

利用“栈”快速计算——逆波兰表达式

Artifact XXXwar exploded Artifact is being deployed, please wait...(已解决)

Thinkphp 5.0.24变量覆盖漏洞导致RCE分析

How to reinstall Win11?One-click method to reinstall Win11

【图像融合】基于加权和金字塔实现图像融合附matlab代码

获取小猪民宿(短租)数据

中缀转后缀、前缀表达式快速解决办法

很多人喜欢用多御安全浏览器,竟是因为这些原因

ES中SQL查询详解

OpenCV DNN blogFromImage() detailed explanation
随机推荐
12306抢票,极限并发带来的思考?
security cross-domain configuration
获取小猪民宿(短租)数据
DVWA靶场环境搭建
字节跳动面试官:请你实现一个大文件上传和断点续传
Several interview questions about golang concurrency
使用 Zadig 交付云原生微服务应用
ES中SQL查询详解
Axure tutorial - the new base (small white strongly recommended!!!)
电机原理动图合集
Flink Yarn Per Job - 提交流程一
Zadig 面向开发者的自测联调子环境技术方案详解
TexturePacker使用文档
Study Notes: The Return of Machine Learning
Chrome书签插件,让你实现高效整理
cdh6 opens oozieWeb page, Oozie web console is disabled.
QML包管理
Appears in oozie on CDH's hue, error submitting Coordinator My Schedule
Classical Literature Reading--DLO
security跨域配置