Java併發程式設計札記-(四)JUC鎖-04Condition簡介
我們已經學習瞭如何通過使用鎖來同步兩個任務,但為了解決某個問題,任務之間只有互斥是不夠的,還需要相互通訊,相互協作。今天就來學習如何實現任務之間的協作。
初識Condition
在任務協作中,關鍵問題是任務之間的通訊。握手可以通過Object的監視器方法(wait()和notify()/notifyAll())和synchronized方法和語句來安全地實現。Java SE5的JUC提供了具有await()和signal()/signalAll()方法的Condition和Lock來實現。其中,Lock代替了synchronized方法和語句,Condition代替了Object的監視器方法。本文介紹Condition。
我們可以通過Condition的await()方法掛起一個任務。當外部條件發生變化,意味著某個任務應該繼續執行時,可以通過signal()來通知某個任務,從而喚醒一個任務,或者呼叫signal()來喚醒所有在這個Condition上被其自身掛起的任務。聽起來Condition的這些方法和Object的監視器方法(wait()和notify()/notifyAll())沒有區別。與Object相比,Condition可以更精細地控制執行緒的休眠與喚醒。Condition實際上被繫結在Lock上,可使用Lock例項的newCondition方法獲取Condition例項。所以我們可以建立多個Condition,在不同的情況下使用不同的Condition。
下面通過JavaDoc中的一個例子來演示下Condition是如何更精細地控制執行緒的休眠與喚醒的。
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();
}
}
}
這是一個有界的緩衝區,支援put(Object)與take()方法。put(Object)負責向緩衝區中存資料,take負責從緩衝區中讀資料。在多執行緒環境下,呼叫put(Object)方法,當緩衝區已滿時,會阻塞寫執行緒,如果緩衝區不滿,則寫入資料,並喚醒讀執行緒。呼叫take()方法時,當緩衝區為空,會阻塞讀執行緒,如果緩衝區不空,則讀取資料,並喚醒寫執行緒。
這就是多個Condition的強大之處,假設快取佇列已滿,那麼阻塞的肯定是寫執行緒,喚醒的肯定是讀執行緒,相反,阻塞的肯定是讀執行緒,喚醒的肯定是寫執行緒。如果採用Object類中的wait(), notify(), notifyAll()實現該緩衝區,當向緩衝區寫入資料之後需要喚醒讀執行緒時,通過notify()或notifyAll()無法明確的指定喚醒讀執行緒,而只能通過notifyAll喚醒所有執行緒,但notifyAll無法區分喚醒的執行緒是讀執行緒,還是寫執行緒。 如果喚醒的是寫執行緒,那麼執行緒剛被喚醒,又被阻塞了,這樣就降低了效率。
方法列表
//方法摘要
void await()
//造成當前執行緒在接到訊號或被中斷之前一直處於等待狀態。
boolean await(long time, TimeUnit unit)
//造成當前執行緒在接到訊號、被中斷或到達指定等待時間之前一直處於等待狀態。
long awaitNanos(long nanosTimeout)
//造成當前執行緒在接到訊號、被中斷或到達指定等待時間之前一直處於等待狀態。
void awaitUninterruptibly()
//造成當前執行緒在接到訊號之前一直處於等待狀態。
boolean awaitUntil(Date deadline)
//造成當前執行緒在接到訊號、被中斷或到達指定最後期限之前一直處於等待狀態。
void signal()
//喚醒一個等待執行緒。
void signalAll()
//喚醒所有等待執行緒。
與Object監視器監視器方法的比較
對比項 | Condition | Object監視器 | 備註 |
---|---|---|---|
使用條件 | 獲取鎖 | 獲取鎖,建立Condition物件 | |
等待佇列的個數 | 一個 | 多個 | |
是否支援通知指定等待佇列 | 支援 | 不支援 | |
是否支援當前執行緒釋放鎖進入等待狀態 | 支援 | 支援 | |
是否支援當前執行緒釋放鎖並進入超時等待狀態 | 支援 | 支援 | |
是否支援當前執行緒釋放鎖並進入等待狀態直到指定最後期限 | 支援 | 不支援 | |
是否支援喚醒等待佇列中的一個任務 | 支援 | 支援 | |
是否支援喚醒等待佇列中的全部任務 | 支援 | 支援 |
本文就講到這裡,想了解Java併發程式設計更多內容請參考: