阻塞隊列類常用語多線程的生產者-消費者模式,作爲生產者和消費者的消息中間件使用,作爲中間件就必須支持阻塞等待作用,在ArrayBlockingQueue 中使用的
阻塞等待工具類是ReentrantLock 類,這個類的功能分析在博主的其他博客中已經通過源碼分析的形式給出,請讀者參考理解。
首先我們看的是ArrayBlockingQueue類的主要屬性和方法:
屬性:
** The queued items */
final Object[] items; //Object數組存放中間值,生產者將數據放入items中,消費者從items中獲取
int takeIndex;// 這是一個索引值,指的是items中消費者要獲取中間值的索引。該索引對應的值存在,消費者獲取索引對應的值後,索引往後移動。
/** items index for next put, offer, or add */
int putIndex; // 這個也是個索引值,指的是生產者要放中間值的索引,放入值後,索引值往後移動,指向下一次生產者放入值的位置索引。索引指向
//的空間值不是中間消息值。
對應這兩個索引和對象數組一起 決定了生產者-消費者之間的值存放邏輯,如下圖所示:
方法:
/**
* Inserts the specified element at the tail of this queue, waiting
* for space to become available if the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
//這個方法會在items存放中間結果值都滿了的時候會阻塞等待
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)//items空間已經滿了
notFull.await();//阻塞等待
insert(e);
} finally {
lock.unlock();
}
}
put方法就用來作爲阻塞插入中間結果,實現了類的自動阻塞。
當然JDK的強大之處就是在於除了滿足開發者需求外,還會提供更多的選擇,函數 public boolean offer(E e, long timeout, TimeUnit unit),這個同put函數一樣有阻塞的功能
而且這個還支持設置等待時間,過了等待時間線程還沒有獲得資源就會插入失敗,返回false。
/**
* Inserts the specified element at the tail of this queue, waiting
* up to the specified wait time for space to become available if
* the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e); //空值校驗
long nanos = unit.toNanos(timeout);//等待時間
final ReentrantLock lock = this.lock;//獲取鎖
lock.lockInterruptibly(); //鎖關閉
try {
while (count == items.length) {
//items滿了
if (nanos <= 0) //如果nanos爲0或負數就終止插入元素e,返回false;
return false;
nanos = notFull.awaitNanos(nanos); //如果等待時間過後還items還是滿的狀態,就返回值0或負數
}
insert(e); //插入中間值操作
return true;
} finally {
lock.unlock();
}
}
offer函數功能是在解決put函數的不足:如果items一直處於滿的狀態或是一直被其他生產者線程競爭下來,那麼put就會一直處於等待狀態;但是offer函數會在給出的等待
時間過後自動返回推出,當然中間值的插入也就失敗了。
與put函數對應生產者獲取中間值的函數:take方法。這個方法會在items中間值數量爲0的時候一直等待下去。
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)//判斷出中間值隊列爲空
notEmpty.await();//等待
return extract();//執行獲取中間值;
} finally {
lock.unlock();
}
}
//爲了解決take函數一直阻塞等待的問題,poll函數就這樣被JDK大神放出來了。
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return extract();
} finally {
lock.unlock();
}
}
這裏就對這個方法不做贅述了。
當然ArrayBlockingQueue類的API還有很多其他方法,其他 的方法就不想本文中提到的4個函數一樣阻塞等待了。各位如果有興趣可以研究下,並自己寫個demo
小結: 這個類中用到的數據結構就是簡單的隊列,IFOF的規則,提供了存值和取值的操作,只是起到了消息隊列中間件的結果,比較簡單。