JUC Collection-LinkedBlockingQueue
参考文献
LinkedBlockingQueue
LinkedBlockingQueue
是在JDK1.5
时,随着J.U.C
包引入的一种阻塞队列,它实现了BlockingQueue
接口,底层基于单链表实现.LinkedBlockingQueue
除了底层数据结构(单链表)与ArrayBlockingQueue
不同外,另外一个特点就是:它维护了两把锁——takeLock
和putLock
。takeLock
用于控制出队的并发,putLock
用于入队的并发。这也就意味着,同一时刻,只能只有一个线程能执行入队/出队操作,其余入队/出队线程会被阻塞;但是,入队和出队之间可以并发执行,即同一时刻,可以同时有一个线程进行入队,另一个线程进行出队,这样就可以提升吞吐量。
属性
1 | public class LinkedBlockingQueue<E> extends AbstractQueue<E> |
核心方法
插入元素 put(E e)
- 插入元素时,首先需要获得**“入队锁”,如果队列满了,则当前线程需要在
notFull
**条件队列等待;否则,将新元素链接到队列尾部
1 | /** |
-
每入队一个元素后,如果队列还没满,则需要唤醒其它可能正在等待的“入队线程”
1
2
3
4
5
6
7/**
* c+1 表示的元素个数.
* 如果,则唤醒一个“入队线程”
*/
c = count.getAndIncrement(); // c表示入队前的队列元素个数
if (c + 1 < capacity) // 入队后队列未满, 则唤醒一个“入队线程”
notFull.signal(); -
向队列中放入元素后,判断队列是否为空。如果队列在放入元素之前为空(即原来的元素个数为0),则需要唤醒一个等待的"出队线程"来处理元素的消费。
1 | if (c == 0) // 入队前队列为空, 则唤醒一个“出队线程” |
删除元素 take()
- 删除元素的逻辑和插入元素类似。删除元素时,首先需要获得**“出队锁”,如果队列为空,则当前线程需要在
notEmpty
**条件队列等待;否则,从队首出队一个元素
1 | /** |
1 | /** |
-
每出队一个元素前,如果队列非空,则需要唤醒其它可能正在等待的“出队线程”
1
2
3c = count.getAndDecrement(); // c表示出队前的元素个数
if (c > 1) // 出队前队列非空, 则唤醒一个出队线程
notEmpty.signal(); -
检查队列在出队操作之前是否已满。如果队列在出队操作之前为满(即原来的元素个数等于队列容量),则需要唤醒一个等待的"入队线程"来处理元素的生产
1
2if (c == capacity) // 队列初始为满,则唤醒一个入队线程
signalNotFull();
总结
- **
LinkedBlockingQueue
和ArrayBlockingQueue
**比较主要有以下区别- 队列大小:
ArrayBlockingQueue
在创建时需要指定固定的容量大小,而LinkedBlockingQueue
可以选择不指定大小(默认为Integer.MAX_VALUE
),使其近似于无界队列。 - 底层数据结构:
ArrayBlockingQueue
使用数组作为底层数据存储容器,而LinkedBlockingQueue
使用单链表作为底层数据存储容器。 - 加锁机制:
ArrayBlockingQueue
使用一个全局锁(ReentrantLock
)来控制入队和出队操作的互斥访问。即同一时间只允许一个线程进行入队或出队操作。而LinkedBlockingQueue
使用锁分离的机制,使用两个独立的锁(putLock
和takeLock
)来控制入队和出队操作的并发访问。这样可以提高并发性能,因为同时可以有一个线程进行入队操作,另一个线程进行出队操作。 - 公平性策略:
ArrayBlockingQueue
可以通过构造函数参数指定公平或非公平的访问策略。而LinkedBlockingQueue
没有提供直接设置公平性策略的选项,它默认为非公平。公平队列会按照线程的到达顺序来获取锁,而非公平队列则允许线程的抢占。
- 队列大小:
https://www.holelin.cn/2023/08/16/java/concurrent/juc/collection/JUC-Collection-LinkedBlockingQueue/
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HoleLin's Blog!