前言
比起ArrayBlockingQueue,LinkedBlockingQueue应该是最被大家常用的阻塞队列,LinkedBlockingQueue是基于链表的一种可选容量的阻塞队列,也就是说,在构造LinkedBlockingQueue实例的时候,你可以像ArrayBlockingQueue那样指定队列大小,也可以不指定大小(这时候默认就是Integer.MAX_VALUE),指定队列的大小是为了防止队列过度的扩张,导致内存被过度占用或溢出。链表的节点是在每一次插入时动态的创建的,除非这会导致队列超出容量限制。LinkedBlockingQueue的容量在实例被构造完成之后也是不允许被更改的。
与ArrayBlockingQueue一样LinkedBlockingQueue不允许插入null值,也是先进先出FIFO队列,队列的头部是队列中存在时间最长的元素,新元素被插入到队尾,队列出队从头部开始。与ArrayBlockingQueue相比,LinkedBlockingQueue通常具有更高的吞吐量,但在大多数并发应用程序中性能的可预测性较差。
LinkedBlockingQueue采用了“双锁队列” 算法,元素的入队和出队分别由putLock、takeLock两个独立的可重入锁来实现。所以比起ArrayBlockingQueue明显提高了吞吐量。
源码分析
先看看其成员变量:
1 static class Node{2 E item;3
4 /**
5 * One of:6 * - the real successor Node7 * - this Node, meaning the successor is head.next8 * - null, meaning there is no successor (this is the last node)9 */
10 Nodenext;11
12 Node(E x) { item =x; }13 }14
15 /**The capacity bound, or Integer.MAX_VALUE if none*/
16 private final intcapacity;17
18 /**Current number of elements*/
19 private final AtomicInteger count = newAtomicInteger();20
21 /**
22 * Head of linked list.23 * Invariant: head.item == null24 */
25 transient Nodehead;26
27 /**
28 * Tail of linked list.29 * Invariant: last.next == null30 */
31 private transient Nodelast;32
33 /**Lock held by take, poll, etc*/
34 private final ReentrantLock takeLock = newReentrantLock();35
36 /**Wait queue for waiting takes*/
37 private final Condition notEmpty =takeLock.newCondition();38
39 /**Lock held by put, offer, etc*/
40 private final ReentrantLock putLock = newReentrantLock();41
42 /**Wait queue for waiting puts*/
43 private final Condition notFull = putLock.newCondition();
View Code
上面的Node节点内部类显然就是用于实现链表的节点实体,item就是当前节点携带的真正对象,next指向下一个节点。head、last分别表示链表的首尾节点,值得注意的是,在LinkedBlockingQueue内部实现的时候,head节点不会参与到链表的实体绑定,也就是说,真正的有效节点挂载都在head节点之后,所以head.item 永远都为null。takeLock和putLock两把锁以及各自的Condition实例分别用于队列元素的出队和入队,可以看到表示队列当前元素个数的count是由一个原子变量来保存的,这是为了避免在维护该变量的时候需要同时获取takeLock、putLock两个锁。当然LinkedBlockingQueue内部还是有一些方法需要同时获取两个锁才能执行,后面会介绍。
LinkedBlockingQueue实例在构造的时候可以指定容量也可以不指定,另外和ArrayBlockingQueue一样也可以在初始化的时候用一个指定的集合初始化队列:
1 public LinkedBlockingQueue(intcapacity) {2 if