2012-05-01 38 views
58

使用條件接口/實現優於常規等待通知機制的優點是什麼?在這裏,我引述Doug Lea的書面意見:條件與等待通知機制

條件因素進行對象監視器方法(等待,通知和notifyAll)分解成不同的對象由他們結合以獲得具有多個等待集每個對象的效果,使用任意的Lock實現。在Lock取代了同步方法和語句的使用的情況下,Condition取代了Object監視器方法的使用。

我看到這是一個更加面向對象的實現等待/通知機制的方式。但是與前者相比有沒有優勢?

回答

19

有很多優勢像上面關於條件接口一些重要的是提到如下:

條件接口自帶兩個額外方法是:

1)boolean awaitUntil(日期截止時間)throws InterruptedException: 導致當前線程等待,直到它被髮信號通知或中斷,或指定的截止時間過去。

2)awaitUninterruptibly(): 導致當前線程等待,直到它被髮送信號。

如果當前線程的中斷狀態在進入此方法時被設置,或者在等待時被中斷,它將繼續等待直到發出信號。當它最終從這個方法返回時,它的中斷狀態仍將被設置。

以上兩種方法都沒有出現在中對象類的默認監視器,在某些情況下,我們要設定的最後期限線程等待,然後我們能夠做到這一點的條件接口。

在我們不希望線程被打斷,並希望當前的線程等待,直到它發出信號,然後我們可以去awaitUninterruptibly方法存在的條件界面的一些情況。

欲瞭解更多信息,條件接口的Java文檔:

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html#awaitUntil%28java.util.Date%29

+1

如果系統時鐘發生變化,awaitUntil(日期截止時間)的行爲將會改變,但它可能不會等待(long timeInMilliseconds) –

27

最大的問題是wait/notify對於新開發人員來說很容易出錯。主要的問題是不知道如何正確處理它們會導致錯誤的bug。

  • 如果您在wait()之前調用notify(),則會丟失它。
  • 可能有時不清楚notify()和wait()是否在同一個對象上被調用。
  • 沒有什麼等待/通知需要狀態更改,但在大多數情況下這是必需的。
  • 等待()可以返回虛假

條件包裝了這個功能集成到一個專用的組件,但它的行爲大致相同。

有關於等待/ nofity這一個,很多之前發佈分鐘的問題,越來越多的Search [java]+wait+notify

+0

感謝您的回答,類/接口的作者意味着什麼「爲每個對象提供多個等待集的效果」? – 100pipers

+2

您可以擁有多個共享相同鎖定的條件。例如http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html等待/通知只有一個「等候集」,即你只能通知任何服務員,而不是特定組。 –

+4

我不確定除了第三點外,Condition有什麼用處。如果在等待前調用信號呼叫丟失,等待仍然可以提前返回,界面中沒有任何內容表示狀態必須改變。 –

26

當您使用Condition: await()/signal()你能辨別哪一個對象或對象組/線程得到一個特定的信號。下面是一個簡單的例子,其中某些線程,生產商,將獲得isEmpty信號,而消費者將獲得isFull信號:

private volatile boolean usedData = true;//mutex for data 
private final Lock lock = new ReentrantLock(); 
private final Condition isEmpty = lock.newCondition(); 
private final Condition isFull = lock.newCondition(); 

public void setData(int data) throws InterruptedException { 
    lock.lock(); 
    try { 
     while(!usedData) {//wait for data to be used 
      isEmpty.await(); 
     } 
     this.data = data; 
     isFull.signal();//broadcast that the data is now full. 
     usedData = false;//tell others I created new data.   
    }finally { 
     lock.unlock();//interrupt or not, release lock 
    }  
} 

public void getData() throws InterruptedException{ 
    lock.lock(); 
    try { 
     while(usedData) {//usedData is lingo for empty 
      isFull.await(); 
     } 
     isEmpty.signal();//tell the producers to produce some more. 
     usedData = true;//tell others I have used the data. 
    }finally {//interrupted or not, always release lock 
     lock.unlock(); 
    }  
} 
+0

- 謝謝,我在發佈http://stackoverflow.com/questions/10407708/signalall-in -condition-interface-vs-notifyall-in-object – 100pipers

+0

很好的例子!信號之前不應該設置usedData = true/false嗎? – AfterWorkGuinness

+0

當您使用wait()/ notify()時,您不會決定通知哪些等待對象/線程:您這樣做。 –

0

@AfterWorkGuinness

你不應該信令

信號後,之前設置usedData =真/假代碼直到鎖塊結束並釋放它,所以順序無關緊要

1

爲了專門解決爲什麼具有多個waitsets是一個優點:

隨着等待/通知是否存在線程正在等待不同的東西(在常見的例子是一個固定的大小阻塞隊列,與一些螺紋把東西隊列和阻塞當隊列已滿,其他線程從隊列中取,然後如果你使用的通知阻塞,當隊列爲空),導致調度挑一個線程等待集中通知,你可以有極端情況選擇的線程對於針對特定情況通知不感興趣。例如隊列會通知給隊列添加一些東西,但是如果選擇的線程是一個生產者並且隊列已滿,那麼它就不能對那個通知進行操作,你寧可去消費者那裏。使用內部鎖定時,您必須使用notifyAll以確保通知不會丟失。

但notifyAll的招流失與每一個電話,每一個地方線程醒來並競爭鎖,但只有一個能夠取得進展。其他線程都在爭奪鎖定,直到一次一個,他們可以獲得鎖定,最有可能返回等待。它會產生很多無益的爭用,最好能夠使用notify並知道只有一個線程被通知,而通知與該線程相關。

這是有獨立的條件等待是一個很大的進步。隊列可以調用條件上的信號,並知道它只會喚醒一個線程,該線程正在等待條件。

API doc for Condition有一個代碼示例顯示了使用多個條件爲界緩衝區,它說:

我們希望繼續等待放線,並採取線程獨立的等待集,這樣我們就可以使用一次只在緩衝區中的項目或空間變爲可用時通知單個線程的優化。