1. wait()、notify()、notifyAll()是java.lang.Object類提供的類函數,用於支持線程間交互
(1)void wait():導致當前線程一直處於等待,直到另外的線程調用這個對象的notify()或者notifyAll()方法,又或者一直等待其他的線程中斷當前等待的線程。
(2)void wait(long timeout): 導致當前線程一直處於等待,直到另外的線程調用這個對象的notify()或者notifyAll()方法,或者等待特定的毫秒數(即timeout), 又或者一直等待其他的線程中斷當前等待的線程。當timeout是負數時,這個方法會拋出java.lang.IllegalArgumentException.
(3)void wait(long timeout, int nanos): 導致當前線程一直處於等待,直到另外的線程調用這個對象的notify()或者notifyAll()方法,或者等待特定的毫秒數(即timeout)和納秒數(即nanos), 又或者一直等待其他的線程中斷當前等待的線程。
(4)void notify():喚醒正在等待該對象監聽器的線程中的一條。
(5)void notifyAll():喚醒正在等待該對象監聽器的全部線程。
若當前線程開始或正在等待通知,任意線程中斷了它,3個wait()方法都會拋出java.lang.InterruptedException。此時,當前線程的中斷狀態會被清除。
2.注意點:
(1)wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。
(2)wait()使當前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊裏使用 wait()、notify/notifyAll() 方法。
synchronized的對象應該是希望上鎖的對象,即那個在多個線程間被共享的對象。在生產者消費者問題中,應該被synchronized的就是那個緩衝區隊列
(3) 由於 wait()、notify/notifyAll() 在synchronized 代碼塊執行,說明當前線程一定是獲取了鎖的。
當線程執行wait()方法時候,會釋放當前的鎖,然後讓出CPU,進入等待狀態。
只有當 notify/notifyAll() 被執行時候,纔會喚醒一個或多個正處於等待狀態的線程,然後繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。
也就是說,notify/notifyAll() 的執行只是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執行情況。所以在編程中,儘量在使用了notify/notifyAll() 後立即退出臨界區,以喚醒其他線程 。
(4)notify 和wait 的順序不能錯,如果A線程先執行notify方法,B線程在執行wait方法,那麼B線程是無法被喚醒的。
3. 在if or while語句中使用wait()、notify()/noityAll()?
永遠在循環中調用,而不在if中調用。
在while循環裏使用wait的目的,是在線程被喚醒的前後都持續檢查條件是否被滿足。如果條件並未改變,wait被調用之前notify的喚醒通知就來了,那麼這個線程並不能保證被喚醒,有可能會導致死鎖問題。而調用wait()之後重新測試條件,保證了安全性。
舉個栗子:
public class K {
//狀態鎖
private Object lock;
//條件變量
private int now,need;
public void produce(int num){
//同步
synchronized (lock){
//當前有的不滿足需要,進行等待
while(now < need){
try {
//等待阻塞
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我被喚醒了!");
}
// 做其他的事情
}
}
}
分析:若改爲 if(now < need) { wait(); } 在某個時刻檢查滿足now < need,但還未執行wait()時,其他線程使用了notify()使得當前線程醒來。那麼,出現了條件沒被滿足,線程被錯誤地喚醒。所以,線程被喚醒後應該再次使用while循環檢查喚醒條件是否被滿足。
標準寫法應該爲:
// The standard idiom for calling the wait method in Java
synchronized (sharedObject) {
while (condition) {
sharedObject.wait();
// (Releases lock, and reacquires on wakeup)
}
// do action based upon condition e.g. take or put into queue
}