按关键词阅读:
问题文章插图
输入图片说明
- LinkedBlockingQueue是什么?
- 优缺点?
- 应用场景?
- 源码实现?
- 个人启发?
所谓双向是指可以从队列的头和尾同时操作 , 并发只是线程安全的实现 , 阻塞允许在入队出队不满足条件时挂起线程 , 这里说的队列是指支持FIFO/FILO实现的链表 。
- 要想支持阻塞功能 , 队列的容量一定是固定的 , 否则无法在入队的时候挂起线程 。 也就是capacity是final类型的 。
- 既然是双向链表 , 每一个结点就需要前后两个引用 , 这样才能将所有元素串联起来 , 支持双向遍历 。 也即需要prev/next两个引用 。
- 双向链表需要头尾同时操作 , 所以需要first/last两个节点 , 当然可以参考LinkedList那样采用一个节点的双向来完成 , 那样实现起来就稍微麻烦点 。
- 既然要支持阻塞功能 , 就需要锁和条件变量来挂起线程 。 这里使用一个锁两个条件变量来完成此功能 。
凡事有利必有弊 。 缺点就是由于独占锁 , 所以不能同时进行两个操作 , 这样性能上就大打折扣 。 从性能的角度讲LinkedBlockingDeque要比LinkedQueue要低很多 , 比CocurrentLinkedQueue就低更多了 , 这在高并发情况下就比较明显了 。
前面分析足够多的Queue实现后 , LinkedBlockingDeque的原理和实现就不值得一提了 , 无非是在独占锁下对一个链表的普通操作 。
核心方法在使用之前 , 让我们一起看一个简单的例子 。
添加元素文章插图
移除元素文章插图
例子我们实现 put 和 take , 分别模拟写入工作者 , 和取出工作者 。
package com.github.houbb.lock.test.lock;import java.util.concurrent.*;/** * @author binbin.hou * @since 1.0.0 */public class LinkedBlockingQueueDemo {private BlockingQueue queue = new LinkedBlockingQueue<>(3);public void put(final String put) throws InterruptedException {System.out.println("设置开始");TimeUnit.SECONDS.sleep(1);queue.put(put);System.out.println("设置完成: " + put);}public void take() throws InterruptedException {System.out.println("获取开始");String take = queue.take();System.out.println("获取成功: " + take);}}
测试代码:public static void main(String[] args) {final LinkedBlockingQueueDemo queueTest = new LinkedBlockingQueueDemo();// 写入线程new Thread(new Runnable() {@Overridepublic void run() {try {for(int i = 0; i < 3; i++) {queueTest.put(i+"T");}} catch (InterruptedException e) {e.printStackTrace();}}}).start();// 读取线程new Thread(new Runnable() {@Overridepublic void run() {try {while (true) {queueTest.take();}} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
日志如下:设置开始获取开始设置完成: 0T获取成功: 0T获取开始设置开始设置完成: 1T获取成功: 1T获取开始设置开始设置完成: 2T获取成功: 2T获取开始
LinkedBlockingQueue 源码文章插图jdk 版本
java version "1.8.0_191"Java(TM) SE Runtime Environment (build 1.8.0_191-b12)Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
算法简介“两个锁队列”算法的一种变体 。putLock设置了看跌期权(和卖出价)的入口 , 并具有等待看跌期权的相关条件 。对于takeLock同样 。
他们俩都依赖的“ count”字段作为原子进行维护 , 以避免在大多数情况下都需要获得两个锁 。
同样 , 为了最大程度地减少获取putLock的需求 , 反之亦然 , 使用了级联通知 。
当认沽权通知其至少启用了一个卖空时 , 它将向买受人发出信号 。
如果自信号发出后输入了更多项目 , 则该接收者又会发信号通知其他人 。
并对称地用于信令放置 。
诸如remove(Object)和迭代器之类的操作均获得这两个锁 。
提供作者和读者之间的可见性 , 如下所示:
每当将元素放入队列时 , 都会获取putLock并更新计数 。
后续的读取器通过获取putLock(通过fullyLock)或通过获取takeLock , 然后读取 n = count.get(); 来保证对排队的节点的可见性 。
这样就可以看到前n个项目 。
稿源:(未知)
【傻大方】网址:http://www.shadafang.com/c/111J2HT2020.html
标题:阻塞队列(3)LinkedBlockingQueue源码详解