1. 程式人生 > >Java併發程式設計札記-(四)JUC鎖-04Condition簡介

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併發程式設計更多內容請參考: