JAVA API中這樣解釋BlockingQueue:
支持兩個附加操作的 Queue
,這兩個操作是:獲取元素時等待隊列變爲非空,以及存儲元素時等待空間變得可用。
BlockingQueue 方法以四種形式出現,對於不能立即滿足但可能在將來某一時刻可以滿足的操作,這四種形式的處理方式不同:第一種是拋出一個異常,第二種是返回一個特殊值(null 或false,具體取決於操作),第三種是在操作可以成功前,無限期地阻塞當前線程,第四種是在放棄前只在給定的最大時間限制內阻塞。下表中總結了這些方法:
拋出異常 | 特殊值 | 阻塞 | 超時 | |
插入 | add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
移除 | remove() |
poll() |
take() |
poll(time, unit) |
檢查 | element() |
peek() |
不可用 | 不可用 |
BlockingQueue 不接受 null 元素。試圖 add、put 或 offer 一個 null 元素時,某些實現會拋出 NullPointerException。null 被用作指示poll 操作失敗的警戒值。
先看一個簡單的實現例子:
public class BoundedBuffer {
//可中斷同步鎖
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length)
putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length)
takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
上面的例子展示了一個定長的隊列,提供了存放和提取數據的方法,且使用ReentrantLock來實現同步。
再來看看SUN的實現:
private static final long serialVersionUID = -817911632652898426L;
private final E[] items;
private int takeIndex;
private int putIndex;
private int count;
private final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
public ArrayBlockingQueue(int paramInt)
{
this(paramInt, false);
}
//初始構造函數
public ArrayBlockingQueue(int paramInt, boolean paramBoolean)
{
if (paramInt <= 0)
throw new IllegalArgumentException();
this.items = ((Object[])new Object[paramInt]);
//初始化鎖對象
this.lock = new ReentrantLock(paramBoolean);
this.notEmpty = this.lock.newCondition();
this.notFull = this.lock.newCondition();
}
//put的實現
public void put(E paramE)
throws InterruptedException
{
if (paramE == null)
throw new NullPointerException();
Object[] arrayOfObject = this.items;
ReentrantLock localReentrantLock = this.lock;
//如果當前線程未被中斷則獲取鎖
localReentrantLock.lockInterruptibly();
try
{
try
{
while (this.count == arrayOfObject.length)
//數組已滿 等待直到被喚醒或中斷
this.notFull.await();
}
catch (InterruptedException localInterruptedException)
{
//被中斷則喚醒
this.notFull.signal();
throw localInterruptedException;
}
insert(paramE);
}
finally
{
localReentrantLock.unlock();
}
}
private void insert(E paramE)
{
this.items[this.putIndex] = paramE;
this.putIndex = inc(this.putIndex);
this.count += 1;
//喚醒持有notEmpty等待的線程
this.notEmpty.signal();
}
public E take()
throws InterruptedException
{
ReentrantLock localReentrantLock = this.lock;
//獲取鎖
localReentrantLock.lockInterruptibly();
try
{
try
{
while (this.count == 0)
//空則等待
this.notEmpty.await();
}
catch (InterruptedException localInterruptedException)
{
//喚醒
this.notEmpty.signal();
throw localInterruptedException;
}
Object localObject1 = extract();
Object localObject2 = localObject1;
return localObject2;
}
finally
{
localReentrantLock.unlock();
}
}
private E extract()
{
Object[] arrayOfObject = this.items;
Object localObject = arrayOfObject[this.takeIndex];
arrayOfObject[this.takeIndex] = null;
this.takeIndex = inc(this.takeIndex);
this.count -= 1;
//喚醒等待的線程
this.notFull.signal();
return localObject;
}
public ArrayBlockingQueue(int paramInt)
{
this(paramInt, false);
}
//初始構造函數
public ArrayBlockingQueue(int paramInt, boolean paramBoolean)
{
if (paramInt <= 0)
throw new IllegalArgumentException();
this.items = ((Object[])new Object[paramInt]);
//初始化鎖對象
this.lock = new ReentrantLock(paramBoolean);
this.notEmpty = this.lock.newCondition();
this.notFull = this.lock.newCondition();
}
//put的實現
public void put(E paramE)
throws InterruptedException
{
if (paramE == null)
throw new NullPointerException();
Object[] arrayOfObject = this.items;
ReentrantLock localReentrantLock = this.lock;
//如果當前線程未被中斷則獲取鎖
localReentrantLock.lockInterruptibly();
try
{
try
{
while (this.count == arrayOfObject.length)
//數組已滿 等待直到被喚醒或中斷
this.notFull.await();
}
catch (InterruptedException localInterruptedException)
{
//被中斷則喚醒
this.notFull.signal();
throw localInterruptedException;
}
insert(paramE);
}
finally
{
localReentrantLock.unlock();
}
}
private void insert(E paramE)
{
this.items[this.putIndex] = paramE;
this.putIndex = inc(this.putIndex);
this.count += 1;
//喚醒持有notEmpty等待的線程
this.notEmpty.signal();
}
public E take()
throws InterruptedException
{
ReentrantLock localReentrantLock = this.lock;
//獲取鎖
localReentrantLock.lockInterruptibly();
try
{
try
{
while (this.count == 0)
//空則等待
this.notEmpty.await();
}
catch (InterruptedException localInterruptedException)
{
//喚醒
this.notEmpty.signal();
throw localInterruptedException;
}
Object localObject1 = extract();
Object localObject2 = localObject1;
return localObject2;
}
finally
{
localReentrantLock.unlock();
}
}
private E extract()
{
Object[] arrayOfObject = this.items;
Object localObject = arrayOfObject[this.takeIndex];
arrayOfObject[this.takeIndex] = null;
this.takeIndex = inc(this.takeIndex);
this.count -= 1;
//喚醒等待的線程
this.notFull.signal();
return localObject;
}
以上只貼出了部分源碼,ArrayBlockingQueue還提供了其他三種處理方式,有興趣的可以自己去研讀。
下面是一個小例子:
public class Setup {
class Producer implements Runnable {
volatile int i=0;
private final BlockingQueue<String> queue;
public Producer(BlockingQueue<String> q) { queue = q; }
public void run() {
try {
while(true&&i<1000) {
queue.put(produce());
}
} catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
String produce(){
i++;
return "put"+i;
}
}
class Consumer implements Runnable {
private final BlockingQueue<String> queue;
Consumer(BlockingQueue<String> q) { queue = q; }
public void run() {
try {
while(true) {
synchronized (lock) {
System.out.println(queue.take());
}
}
} catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
Object lock = new Object();
public static void main(String args[]){
Setup s = new Setup();
BlockingQueue<String> q = new ArrayBlockingQueue<String>(1);
Producer p = s.new Producer(q);
Consumer c1 = s.new Consumer(q);
Consumer c2 = s.new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}