Concurrency Item - 用併發工具替換wait和notify

標題起得有些奇怪,好端端的爲什麼要替換wait和notify?



在論壇看到了這麼一段:

14. 爲什麼wait(), notify()和notifyAll()必須在同步方法或者同步塊中被調用?
當一個線程需要調用對象的wait()方法的時候,這個線程必須擁有該對象的鎖,接着它就會釋放這個對象鎖並進入等待狀態直到其他線程調用這個對象上的notify()方法。同樣的,當一個線程需要調用對象的notify()方法時,它會釋放這個對象的鎖,以便其他在等待的線程就可以得到這個對象鎖。由於所有的這些方法都需要線程持有對象的鎖,這樣就只能通過同步來實現,所以他們只能在同步方法或者同步塊中被調用。



但這並不是Java語義上的限制,即便寫成如下這種形式(摘自爆棧某人提問:wait/notify這玩意兒到底怎麼用?),編譯仍然會通過無誤:

public void waiting() {
	for (int i = 0; i < 10; i++) {
		if (i == 5)
			try {
				this.wait();
			} catch (InterruptedException e) {

        		}
                else
	        	System.out.println(i);
	}
	System.out.println("notify me now");
	this.notify();
}



我不認爲會有人在調用某個方法時總是先確認一下javadoc....我認爲從IDE列出來的方法提示裏選擇一個像那麼回事的方法執行一遍再確認文檔比較符合正常人的習慣。

有人提出這樣的問題也說明了一個問題:wait/notify用起來確實很晦澀。



如果覺得還不夠晦澀,認爲"只要在synchronized塊裏面用就可以了",其實並不是這樣,參考javadoc中的一段:

A thread can also wake up without being notified, interrupted, or 

timing out, a so-called spurious wakeup.  While this will rarely 

occur in practice, applications must guard against it by testing for 

the condition that should have caused the thread to be awakened, and 

continuing to wait if the condition is not satisfied.  In other words,

waits should always occur in loops, like this one:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait(timeout);
        // ... Perform action appropriate to condition
}



下面是一個線程莫名甦醒的情況:

  1. 在某個線程調用notify後和等待線程甦醒的這段時間之內另一個線程得到了鎖並改變了受保護的狀態。

  2. 其他線程惡意(也就是破壞可變性條件)調用notify或者直接notifyAll.

  3. 僞喚醒 (http://en.wikipedia.org/wiki/Spurious_wakeup).



自Java 1.5發行,JDK提供了更高級的併發工具,這些工具可以更優雅地解決以往只有wait+notify才能解決的問題。

所謂“更高級的併發工具”包括三類:

  1. Executor Framework 

  2. 併發集合

  3. 同步器 (Latch,Barrier,Semaphor什麼的...)



對於新代碼,如今幾乎很少有需要使用wait/notify的情況。

如果必須使用wait/notify,則需要保證是在循環裏調用wait,儘管這感覺很蠢。

而爲了防止活躍性問題,還是多加利用Java 5之後的各種工具吧。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章