java-阻塞佇列之LinkedBlockingQueue
概述
LinkedBlockingQueue
內部由單鏈表實現
,只能從head取元素,從tail新增元素。新增元素和獲取元素都有獨立的鎖,也就是說LinkedBlockingQueue是讀寫分離的
,讀寫操作可以並行執行。LinkedBlockingQueue採用可重入鎖ReentrantLock
來保證在併發情況下的執行緒安全。
構造器
LinkedBlockingQueue
一共有三個構造器,分別是無參構造器、可以指定容量的構造器、可以穿入一個容器的構造器。如果在建立例項的時候呼叫的是無參構造器,LinkedBlockingQueue
的預設容量是Integer.MAX_VALUE
,這樣做很可能會導致佇列還沒有滿,但是記憶體卻已經滿了的情況(記憶體溢位)。
public LinkedBlockingQueue(); //設定容量為Integer.MAX
public LinkedBlockingQueue(int capacity); //設定指定容量
public LinkedBlockingQueue(Collection<? extends E> c); //穿入一個容器,如果呼叫該構造器,容量預設也是Integer.MAX_VALUE
LinkedBlockingQueue常用操作
取資料
take()
:首選。當佇列為空時阻塞
poll()
:彈出隊頂元素,佇列為空時,返回空
peek()
:和poll烈性,返回隊隊頂元素,但頂元素不彈出。佇列為空時返回null
remove(Object o)
:移除某個元素,佇列為空時丟擲異常。成功移除返回true
新增資料
put()
:首選。隊滿是阻塞
offer()
:隊滿時返回false
判斷佇列是否為空
size()
方法會遍歷整個佇列,時間複雜度為O(n),所以最好選用isEmtpy
put元素原理
基本過程:
1.判斷元素是否為null,為null丟擲異常
2.加鎖(可中斷鎖)
3.判斷佇列長度是否到達容量,如果到達一直等待
4.如果沒有隊滿,enqueue()在隊尾加入元素
5.佇列長度加1,此時如果佇列還沒有滿,呼叫signal喚醒其他堵塞佇列
if (e == null) throw new NullPointerException(); int c = -1; Node<E> node = new Node<E>(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(); }
take元素原理
基本過程:
1.加鎖(依舊是ReentrantLock),注意這裡的鎖和寫入是不同的兩把鎖
2.判斷佇列是否為空,如果為空就一直等待
3.通過dequeue方法取得資料
3.取走元素後佇列是否為空,如果不為空喚醒其他等待中的佇列
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;
}
enqueue()和dequeue()方法實現都比較簡單,無非就是將元素新增到隊尾,從隊頂取走元素,感興趣的朋友可以自己去看一下,這裡就不貼上了。