概念
LinkedBlockingQueue是非固定容量队列,内部是通过链表存放数据,并通过两把互斥锁(分别为取时互斥锁和放时互斥锁,这也是与ArrayBlockingQueue的本质区别,这样可以提高性能)来控制访问数据的同步问题。提供的方法与ArrayBlockingQueue基本是一致的,下面只介绍下有差异的地方,细节上看ArrayBlockingQueue的源码解读就行。
核心属性
/*** 阻塞队列元素封装为Node*/
static class Node<E> {E item;Node<E> next;Node(E x) { item = x; }
}/** 指定队列的长度,默认为Integer.MAX */
private final int capacity;/** 记录数据条数 */
private final AtomicInteger count = new AtomicInteger();//指向链表头
transient Node<E> head;
//指向链表尾
private transient Node<E> last;/** 读锁 */
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();/** 写锁 */
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
2.1 写操作
// 写操作
public boolean offer(E e) {// 非空if (e == null) throw new NullPointerException();// 拿到count(记录当前数据条数)final AtomicInteger count = this.count;// 如果count达到了最大值if (count.get() == capacity)// 链表满了,添加失败,直接返回。return false;// 声明int c = -1;// 用当前元素创建一个新的NodeNode<E> node = new Node<E>(e);// 写锁final ReentrantLock putLock = this.putLock;putLock.lock();try {// 再次判断链表是否还有空间,有则通过enqueue存入数据if (count.get() < capacity) {// 数据存入操作enqueue(node);// 链表元素数量count加1c = count.getAndIncrement();// 添加完数据之后,长度依然小于最大长度,链表还有存放空间,唤醒其他阻塞的写线程 // 读写不互斥,可能前面在执行时,队列是满的,但是读操作依然在进行if (c + 1 < capacity)notFull.signal();}} finally {putLock.unlock(); //解写锁}// 判断原先是否为空,c为0,表示原先链表为空,读线程肯定都在阻塞,所以,此时需求唤醒阻塞的读线程if (c == 0)signalNotEmpty();return c >= 0;
}// 插入数据到链表(尾插法)
private void enqueue(Node<E> node) {last = last.next = node;
}
2.2 读操作
public E poll() {final AtomicInteger count = this.count;// 为0,没数据,退出if (count.get() == 0)return null;E x = null;int c = -1;// 读锁final ReentrantLock takeLock = this.takeLock;takeLock.lock();try {// 如果队列有数据 if (count.get() > 0) {x = dequeue();// count --c = count.getAndDecrement();if (c > 1)// c > 1,说明还有数据,唤醒其他阻塞的读线程notEmpty.signal();}} finally {takeLock.unlock();}if (c == capacity)// 到这说明链接有空位,唤醒写线程signalNotFull();return x;
}
// 从头部取数据
private E dequeue() {Node<E> h = head;Node<E> first = h.next;h.next = h; // help GChead = first;E x = first.item;first.item = null;return x;
}