Java併發編程——wait()、notify()、notifyAll()

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 
}

 

 

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