LinkedBlockingQueue
LinkedBlockingQueue隊列跟ArrayBlockingQueue一樣都實現了BlockingQueue。因此同樣是阻塞隊列,有三種刪除和三種添加的方法。LinkedBlockingQueue的底層是基於鏈表實現。ArrayBlockingQueue通過一個鎖和鎖的兩個條件對象保證併發的安全性,LinkedBlockingQueue通過兩個鎖和每個鎖的一個條件隊列來控制併發的安全性。
基本屬性如下:
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
private final int capacity;
private final AtomicInteger count = new AtomicInteger(0);
private transient Node<E> head;
private transient Node<E> last;
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
ArrayBlockingQueue只有一個鎖,因此不支持添加和刪除元素並行執行,LinkedBlockingQueue有一個取鎖和放鎖,因此支持並行的取和放。但是,取和取,放和放之間是互斥的。每次只能有一個線程拿到鎖。
add方法實現
同理add方法是父類AbstractQueue隊列實現,其中調用了offer方法。如果因爲隊列滿了,添加失敗拋出IllegalStateException異常。
offer實現
public boolean offer(E e) {
if (e == null) throw new NullPointerException();//插入元素爲空拋出異常
final AtomicInteger count = this.count;
if (count.get() == capacity)//隊列已滿,返回false
return false;
int c = -1;
Node<E> node = new Node(e); //把需要插入的對象封裝成隊列的一個節點
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.unlock(); //釋放鎖
}
if (c == 0) //如果先前隊列爲空(開始線程爲空,那麼可能所有取線程在調用take方法時候,會堵塞起來),通知被堵塞的取線程
signalNotEmpty();
return c >= 0;
}
enqueue方法
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
put方法
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();//插入對象爲空拋出異常
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();
}
poll方法
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0) //如果隊列爲空,返回null
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock(); //隊列不爲空,獲取取鎖
try {
if (count.get() > 0) { //再次判斷隊列是否有元素
x = dequeue(); //獲取頭節點的值,並且刪除
c = count.getAndDecrement();
if (c > 1) //隊列如果還有元素,通知其它取線程
notEmpty.signal();
}
} finally {
takeLock.unlock(); //釋放鎖
}
if (c == capacity) //這裏看上去c == capacity看上去是隊列滿了,就通知放線程,但是這裏的c返回的是count取元素之前的值。之後,這個count減1.所以這個時候count的值可能是在一直變化的。
signalNotFull();
return x;
}
take方法
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(); //元素個數減1,並返回原來的個數
if (c > 1) //如果隊列沒不爲空,通知其它取線程
notEmpty.signal();
} finally {
takeLock.unlock();//釋放鎖
}
if (c == capacity)//還清其它放線程。
signalNotFull();
return x;
}
remove方法
public boolean remove(Object o) {
if (o == null) return false;//對象爲空,返回false
fullyLock();//刪除元素,此元素的位置任意,所以取鎖和放鎖都鎖定
try {
for (Node<E> trail = head, p = trail.next; //遍歷整個鏈表
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) { //判斷是否找到元素
unlink(p, trail); //找到元素,調用unlink方法
return true; //刪除成功返回true
}
}
return false;
} finally {
fullyUnlock();//釋放鎖
}
}
unlink
void unlink(Node<E> p, Node<E> trail) {
p.item = null; //將找到的元素置空
trail.next = p.next;//把刪除元素的上一個節點和它下一個節點鏈接(等於把自己從鏈表中斷開出來)
if (last == p)//如果元素是最後一個,就把last節點指向它的上一個節點
last = trail;
if (count.getAndDecrement() == capacity)//喚醒放節點
notFull.signal();
}
LinkedBlockingQueue隊列還有一個獲取頭部的方法,但那是該方法並不移除元素。這就是peek方法
public E peek() {
if (count.get() == 0)//隊列爲空,返回null
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();//獲取鎖
try {
Node<E> first = head.next;
if (first == null) //頭爲空返回null
return null;
else
return first.item;//不爲空返回其值
} finally {
takeLock.unlock();
}
}