LinkedBlockingQueue类
一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。 可选的容量范围构造方法参数作为防止队列过度扩展的一种方法。如果未指定容量,则它等于 Integer.MAX_VALUE。除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点。
这个类的常见用法,应该用过的都应该知道了,就不再举例了,直接进入源码的分析。在分析其他方法前,先看下类变量
/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger(0);
/** Head of linked list */
private transient Node<E> head;
/** Tail of linked list */
private transient Node<E> 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();
其中有两个锁和两个条件变量是最重要的,决定了下面的方法实现。。阻塞的效果是靠这些来实现的。具体看下面的方法分析。
new 创建对象
/**
* Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
* {@link Integer#MAX_VALUE}. 从类名可以看出它主要是用链表来保存数据的。。下面的分析也可以看出来确实如此
*
*/
public LinkedBlockingQueue() {
// 这里可以看出这个类是有容量限制的,默认是最大容量 int.max
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.capacity = capacity;
// 这里可以看出链表的头和尾默认都是null节点
last = head = new Node<E>(null);
}
/**
* Linked list node class 这里是链表的节点实现了
*/
static class Node<E> {
/** The item, volatile to ensure barrier separating write and read */
// 这里的volatile变量确保了多线程中内存的一致性
volatile E item;
Node<E> next;
Node(E x) {
item = x;
}
}
put方法
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;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
// put 加锁
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.
*/
try {
// 判断是否满了,如果满了则notFull 线程等待
while (count.get() == capacity)
notFull.await();
} catch (InterruptedException ie) {
notFull.signal(); // propagate to a non-interrupted thread
throw ie;
}
// 插入链表
insert(e);
// 更新容量
c = count.getAndIncrement();
// 没满则唤醒notFull线程
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
// 当c==0代表容量刚从空转为非空状态,则唤醒非空线程
if (c == 0)
signalNotEmpty();
}
/**
* Signals a waiting take. Called only from put/offer (which do not
* otherwise ordinarily lock takeLock.)
*/
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 唤醒非空线程
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
take 方法
// 这里的加锁方式刚好和put 方法相反。就不多说了。
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
try {
while (count.get() == 0)
notEmpty.await();
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to a non-interrupted thread
throw ie;
}
x = extract();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
poll 方法
// 这个其实和poll()无参数的方法类似,只是多了for循环的一个计数的功能。poll()就不分析了。
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
for (;;) {// 这个是为了计时而做的循环
if (count.get() > 0) {
x = extract();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
break;
}
if (nanos <= 0)
return null;
try {
// awaitNanos 方法返回的是nanosTimeout
// 值减去花费在等待此方法的返回结果的时间的估算,相当于就是剩余时间了。
nanos = notEmpty.awaitNanos(nanos);
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to a non-interrupted thread
throw ie;
}
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
offer方法
// 这里和poll(long timeout, TimeUnit unit)方法的锁的实现相反。。也不多说了。
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null)
throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
for (;;) {
if (count.get() < capacity) {
insert(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
break;
}
if (nanos <= 0)
return false;
try {
nanos = notFull.awaitNanos(nanos);
} catch (InterruptedException ie) {
notFull.signal(); // propagate to a non-interrupted thread
throw ie;
}
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
可以看出LinkedBlockingQueue 类的实现比较简单,也很容易理解,灵活应用了条件变量(Condition),减少了锁的竞争。。 by zhxing