1.常用的阻塞队列
其中LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue
2.LinkedBlockingQueue原理
3.LinkedBlockingQueue的几个关键属性
static class Node {
E item;
/**后继节点
*/
Node next;
Node(E x) { item = x; }
}
/** 队列容量,默认最大,可指定大小 */
private final int capacity;
/** 当前容量 */
private final AtomicInteger count = new AtomicInteger();
/**
* 头节点.
* Invariant: head.item == null
*/
transient Node head;
/**
* 尾节点.
* Invariant: last.next == null
*/
private transient Node last;
/** 定义的出队和入队分离锁,2个队列空和满的出队和入队条件 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();
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* 指定有界大小,同时初始化head和tail节点
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity} is not greater
* than zero
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node(null);
}
/**
* 遍历集合元素,放到队列进行初始化 --- 无界队列
*
* @param c the collection of elements to initially contain
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
public LinkedBlockingQueue(Collection c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(new Node(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
4.BlockingQueue源码分析
//入队,将元素添加到对尾等价 last.next = node; last = last.next
private void enqueue(Node node) {
last = last.next = node;
}
/**
* 出队,从头部出
*
* @return the node
*/
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node h = head;
Node first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
// 队列已满:false
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity) // 队列容量达到最大值,添加失败
return false;
int c = -1;
Node node = new Node(e);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // 获取插入锁putLock
try {
if (count.get() < capacity) { // 加锁后再次判断队列是否已满
enqueue(node); // 入队
c = count.getAndIncrement(); // 返回Inc之前的值
if (c + 1 < capacity) // 插入节点后队列未满
notFull.signal(); // 唤醒notFull上的等待线程
}
} finally {
putLock.unlock(); // 释放插入锁
}
if (c == 0)
signalNotEmpty(); // 如果offer前队列为空,则唤醒notEmpty上的等待线程
return c >= 0;
}
long nanos = unit.toNanos(timeout); //将指定的时间长度转换为毫秒来进行处理
while (count.get() == capacity) {
if (nanos <= 0) // 等待的剩余时间小于等于0,那么直接返回false
return false;
nanos = notFull.awaitNanos(nanos); // 最多等待时间(纳秒)
}
//插入节点:\n线程入队操作前会获取putLock锁,插入数据完毕后释放;
队列未满将新建Node节点,添加到队列末尾;
队列已满则阻塞线程(notFull.await())或返回false;若线程B取出数据,则会调用notFull.signal()唤醒notFull上的等待线程(线程A继续插数据)。
若入队前队列为空,则唤醒notEmpty上等待的获取数据的线程
// 一直阻塞直到插入成功
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node node = new Node(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
// 可中断的锁获取操作(优先考虑响应中断),如果线程由于获取锁而处于Blocked状态时,线程将被中断而不再继续等待(throws InterruptedException),可避免死锁。
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
// 队列若满线程将处于等待状态。while循环可避免“伪唤醒”(线程被唤醒时队列大小依旧达到最大值)
while (count.get() == capacity) {
notFull.await(); // notFull:入队条件
}
enqueue(node); // 将node链接到队列尾部
c = count.getAndIncrement(); // 元素入队后队列元素总和
if (c + 1 < capacity) // 队列未满
notFull.signal(); // 唤醒其他执行入队列的线程
} finally {
putLock.unlock(); // 释放锁
}
// c=0说明队列之前为空,出队列线程均处于等待状态。添加一个元素后,队列已不为空,于是唤醒等待获取元素的线程
if (c == 0)
signalNotEmpty();
}
参考、感谢:
http://blog.csdn.net/u010887744/article/details/73010691