阻塞隊列
LinkedBlockingQueue
當隊列爲空,消費者線程被阻塞;當隊列裝滿,生產者線程被阻塞。這是阻塞隊列的基本特點。LinkedBlockingQueue
對鎖進行了優化,就是put鎖和take鎖分離。
另外,線程協作通知方式,不都是生產者直接喚醒全部消費者,而是生產者通知一個消費者線程,消費者線程發現還有可取的元素,會通知其他消費者線程。稱爲Cascading notification。
下面對源碼進行簡要分析:
// 鏈表節點數,這裏final代表的是引用不可變,而不是值不可變
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
transient Node<E> head;
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node<E> last;
// take鎖,消費者線程需要的鎖
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
// put鎖,生產者需要的鎖
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
到這裏位置,阻塞隊列的狀態基本定義完了,下面看簡單的生產者線程的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;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
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 (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
//這裏就是生產者線程通知其他生產者線程,隊列沒滿
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
// 這裏是通知消費者線程
signalNotEmpty();
}