LinkedBlockingQueue是一个用链表实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。
此队列的默认和最大长度是Integer.MAX_VALUE,LinkedBlockingQueue类有三个构造方法:
// 默认构造方法,该方法会调用this(Integer.MAX_VALUE),即默认最大长度是Integer.MAX_VALUE
public LinkedBlockingQueue();
// 参数capacity为指定的队列最大长度
public LinkedBlockingQueue(int capacity);
// 根据Collection来创建队列,会将集合中的元素加入到队列中
public LinkedBlockingQueue(Collection extends E> c);
LinkedBlockingQueue类定义为:
public class LinkedBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable
该类同样继承了AbstractQueue抽象类并实现了BlockingQueue接口,这里不再叙述。
LinkedBlockingQueue类中的数据都被封装成了Node对象:
static class Node {
// 节点数据
E item;
// 下一节点
Node next;
Node(E x) { item = x; }
}
同时,LinkedBlockingQueue类通过ReentrantLock和Condition来确保多线程环境下的同步问题。
/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
transient Node head;
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node last;
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
LinkedBlockingQueue类有两个独占锁:takeLock和putLock,也就是说,添加和删除元素并不是互斥操作,可以同时进行,这样也就可以提高吞吐量。
我们来看一下add(E e)方法:
LinkedBlockingQueue类的add(E e)方法继承自AbstractQueue:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
LinkedBlockingQueue类实现的offer(E e)方法为:
public boolean offer(E e) {
// 若插入的数据为null,则抛出NullPointerException异常
if (e == null) throw new NullPointerException();
// 获取队列中的元素个数
final AtomicInteger count = this.count;
// 若队列已满则返回false
if (count.get() == capacity)
return false;
int c = -1;
// 将对象构建为Node节点
Node node = new Node(e);
// 获取putLock独占锁
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
// 若队列未满
if (count.get() < capacity) {
// 添加元素
enqueue(node);
// 更新元素个数值
c = count.getAndIncrement();
// 若队列仍然未满,唤醒阻塞在非满条件上的线程
if (c + 1 < capacity)
notFull.signal();
}
} finally {
// 释放putLock独占锁
putLock.unlock();
}
// 队列中还有一条数据(c的初始值为-1),唤醒阻塞在非空条件上的线程
if (c == 0)
signalNotEmpty();
return c >= 0;
}
LinkedBlockingQueue类的offer(E e)方法与ArrayBlockingQueue类相比,主要有两处不同:
1、ArrayBlockingQueue类在添加元素之后,会唤醒消费线程,而LinkedBlockingQueue类在添加完元素之后,若队列未满,它会唤醒其他的生产进程,产生该差异的主要原因是ArrayBlockingQueue使用了一个独占锁,而LinkedBlockingQueue使用了两个独占锁,这两个锁可以分别控制元素入队与出队操作,而不会产生同步问题,可以提高吞吐量。
2、LinkedBlockingQueue类在添加完元素之后,也可能会唤醒消费线程,唤醒的前提是c==0,那为什么是这样呢?这也与两个独占锁有关,因为消费线程在消费数据时,只会获取takeLock,所以说,生产线程生产数据时,不会影响消费线程,若队列中的元素一直>0,消费线程是不会停止的,可能没有消费线程停止 。而在c=0的时,这表明,可能有消费线程已经停止,这时,就需要唤醒消费线程,来取出队列中的数据。
我们这边来看一下poll()方法:
public E poll() {
// 获取队列的元素个数
final AtomicInteger count = this.count;
// 队列为空,则直接返回null
if (count.get() == 0)
return null;
E x = null;
int c = -1;
// 获取takeLock独占锁
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 若队列非空
if (count.get() > 0) {
// 元素出队
x = dequeue();
// 更新元素个数值
c = count.getAndDecrement();
// 如果队列非空,则唤醒阻塞在非空条件上的线程
if (c > 1)
notEmpty.signal();
}
} finally {
// 释放takeLock独占锁
takeLock.unlock();
}
// 若队列已满,则唤醒阻塞在非满条件上的线程
if (c == capacity)
signalNotFull();
return x;
}
该类的poll()方法与ArrayBlockingQueue类相比,仍然有两处不同,具体原因不再解释,类似于offer(E e)方法。
因为LinkedBlockingQueue类具有两个独占锁,该类的其他方法与ArrayBlockingQueue相比,也有一些不同,比如remove(Object o)方法:
public boolean remove(Object o) {
// 若要删除的元素为null,则直接返回false
if (o == null) return false;
// 获取两个独占锁
fullyLock();
try {
// 遍历查找要删除的节点,将其删除,成功返回true,否则,返回false
for (Node trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) {
unlink(p, trail);
return true;
}
}
return false;
} finally {
// 释放两个独占锁
fullyUnlock();
}
}
我们看到该方法调用了一个fullyLock()方法
void fullyLock() {
putLock.lock();
takeLock.lock();
}
该方法是获取两个独占锁,为什么要或者这两个锁呢?因为remove(Object o)方法删除的元素的位置不确定,可能会产生同步问题,所以要获取两个锁。
Java并发编程之ArrayBlockingQueue阻塞队列详解
Java并发编程之ReentrantLock详解
Java并发编程之Condition详解
方腾飞:《Java并发编程的艺术》