更多 Java 并發編程方面的文章,請參見文集《Java 并發編程》
阻塞隊列 Blocking Queue
- 當隊列空時,獲取元素的線程會等待
- 當隊列滿時,存儲元素的線程會等待
提供的方法:
-
插入元素:
- add(e):拋出異常
- offer(e):返回特殊值
- put(e):一直阻塞
- offer(e,time,unit):超時退出
-
移除元素:
- remove():拋出異常
- poll():返回特殊值
- take():一直阻塞
- poll(time,unit):超時退出
JDK 7 提供了 7 個阻塞隊列
-
ArrayBlockingQueue :一個由數組結構組成的 有界 阻塞隊列。
- 此隊列按照先進先出(FIFO)的原則對元素進行排序。
- 默認情況下不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的所有生產者線程或消費者線程,當隊列可用時,可以按照阻塞的先后順序訪問隊列,即先阻塞的生產者線程,可以先往隊列里插入元素,先阻塞的消費者線程,可以先從隊列里獲取元素。通常情況下為了保證公平性會降低吞吐量。我們可以使用以下代碼創建一個公平的阻塞隊列:
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
-
LinkedBlockingQueue :一個由鏈表結構組成的 有界 阻塞隊列。
- 此隊列按照先進先出(FIFO)的原則對元素進行排序。
-
PriorityBlockingQueue :一個支持優先級排序的 無界 阻塞隊列。
- 默認情況下元素采取自然順序排列,也可以通過比較器 comparator 來指定元素的排序規則。元素按照升序排列。
- DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
- SynchronousQueue:一個不存儲元素的阻塞隊列。
- LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
- LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。
關于 通過 wait 和 notify 實現生產者消費者模式,可以參考 鏈接。
關于 通過 Lock 和 競爭條件 Condition 實現生產者消費者模式,可以參考 鏈接。
利用阻塞隊列實現生產者消費者模式,代碼如下:
public class BlockingQueue_Test {
private static final int MAX_CAPACITY = 10;
private static ArrayBlockingQueue<Object> goods = new ArrayBlockingQueue<Object>(MAX_CAPACITY);
public static void main(String[] args) {
(new ProducerThread()).start();
(new ConsumerThread()).start();
}
static class ProducerThread extends Thread {
public void run() {
while (true) {
// 每隔 1000 毫秒生產一個商品
try {
Thread.sleep(1000);
goods.put(new Object());
System.out.println("Produce goods, total: " + goods.size());
} catch (InterruptedException e) {
}
}
}
}
static class ConsumerThread extends Thread {
public void run() {
while (true) {
// 每隔 500 毫秒消費一個商品
try {
Thread.sleep(500);
goods.take();
System.out.println("Consume goods, total: " + goods.size());
} catch (InterruptedException e) {
}
}
}
}
}
阻塞隊列的實現原理
以 ArrayBlockingQueue
為例,實際上使用了 ReentrantLock 和 Condition。
構造方法:
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
插入元素,如果隊列已滿,則阻塞 notFull.await();:
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
移除元素,如果隊列已空,則阻塞 notEmpty.await();:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}