LinkedBlockingQueue實現了BlockingQueue接口以及Serializable接口,是有序的FIFO隊列,構造函數中,可傳入一個最大容量值,如果沒有傳入,則默認是Integer.MAX_VALUE
一 首先看一下重要的幾個類變量:
/** 保存當前隊列中元素的個數 */
private final AtomicInteger count = new AtomicInteger(0);
/**
* 頭元素
* Invariant: head.item == null
*/
private transient Node<E> head;
/**
* 尾元素
* Invariant: last.next == null
*/
private transient Node<E> last;
/** 消費者鎖,Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** 使消費者線程等待,直到被喚醒或者打斷 */
private final Condition notEmpty = takeLock.newCondition();
/** 生產者鎖,Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** 使生產者線程等待,直到被喚醒或者打斷 */
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);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
執行過程如下:
1 如果傳入元素爲空,拋出空指針異常
2 獲得put的鎖,以及原子的count,然後lock,注意,是可打斷的lock
3 判斷當前隊列是否飽和,若飽和,生產者線程進入等待狀態
4 如果隊列不飽和,則將元素包裝爲一個node放到隊列中
5 count+1,如果count+1仍然小於隊列的最大容量,則生產者線程被喚醒
6 在finnally中釋放鎖,最後喚醒消費者,提醒消費者可以從隊列中取對象了
三 offer方法
offer提供了兩個方法,一個方法是可傳入等待時間,另一個則沒有
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 {
while (count.get() == capacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
執行過程如下:
1 如果傳入元素爲空,拋出空指針異常
2 根據傳入的timeout和unit計算出最長等待的時間
3 獲得put的鎖,以及原子的count,表示當前隊列中的元素個數,然後lock,注意,是可打斷的lock
4 如果隊列飽和,則超過等待時間後,直接返回false
5 以下處理構成同put
另外一個不帶參數的方法,如果判斷隊列飽和,直接返回false,不阻塞
四 take方法
java代碼
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
執行過程如下:
1 獲得put的鎖,以及原子的count,表示當前隊列中的元素個數,然後lock,注意,是可打斷的lock
2 如果當前隊列爲空,則阻塞
3 如果非空,則元素出列,count-1,如果count>1,表示隊列非空,則消費者線程被喚醒
4 在finally中釋放lock,喚醒生產者生產
五 pool方法
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 {
while (count.get() == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
執行過程如下:
1 獲得put的鎖,以及原子的count,表示當前隊列中的元素個數,然後lock,注意,是可打斷的lock
2 如果當前隊列爲空,則線程阻塞,並等待指定時間,如果在指定時間還是空,則直接返回空
3 以下過程同take
pool也提供了不帶參數的方法,表示如果隊列爲空,則直接返回null,不阻塞
六 peek
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
不阻塞,不移除隊首元素
個人學習總結:
根據以上源碼分析,得出方法之間的異同:
(1) 生產者方法:put,offer
(2)消費者方法:take,poll,peek
(3)put方法中,如果隊列始終飽和,則當前線程會一直等待,直到有對象出列
offer(可傳入等待時間),如果隊列飽滿,會等待指定的時間,如果在指定時間內還飽滿,則直接返回false
offer(沒有參數),如果隊列飽滿,直接返回false,線程不等待
put和offer(可傳入等待時間)都是可被打斷的
(4)take在隊列爲空時,會始終阻塞
poll分爲帶等待時間和不帶的,如果不帶等待時間,則不阻塞,移除隊首元素(FIFO)
peek是不移除隊首元素,不阻塞