Java的wait(), notify()和notifyAll()使用心得
本篇文章是對java的 wait(),notify(),notifyAll()進行了詳細的分析介紹,需要的朋友參考下。
wait(),notify()和notifyAll()都是java.lang.Object的方法:
wait(): Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
notify(): Wakes up a single thread that is waiting on this object’s monitor.
notifyAll(): Wakes up all threads that are waiting on this object’s monitor.
這三個方法,都是Java語言提供的實現執行緒間阻塞(Blocking)和控制程式內排程(inter-process communication)的底層機制。在解釋如何使用前,先說明一下兩點:
1. 正如Java內任何物件都能成為鎖(Lock)一樣,任何物件也都能成為條件佇列(Condition queue)。而這個物件裡的wait(), notify()和notifyAll()則是這個條件佇列的固有(intrinsic)的方法。
2. 一個物件的固有鎖和它的固有條件佇列是相關的,為了呼叫物件X內條件佇列的方法,你必須獲得物件X的鎖。這是因為等待狀態條件的機制和保證狀態連續性的機制是緊密的結合在一起的。
(An object’s intrinsic lock and its intrinsic condition queue are related: in order to call any of the condition queue methods on object X, you must hold the lock on X. This is because the mechanism for waiting for state-based conditions is necessarily tightly bound to the mechanism fo preserving state consistency)
根據上述兩點,在呼叫wait(), notify()或notifyAll()的時候,必須先獲得鎖,且狀態變數須由該鎖保護,而固有鎖物件與固有條件佇列物件又是同一個物件。也就是說,要在某個物件上執行wait,notify,先必須鎖定該物件,而對應的狀態變數也是由該物件鎖保護的。
知道怎麼使用後,我們來問下面的問題:
1. 執行wait, notify時,不獲得鎖會如何?
請看程式碼:
public static void main(String[] args) throws InterruptedException { Object obj = new Object(); obj.wait(); obj.notifyAll(); }
執行以上程式碼,會丟擲java.lang.IllegalMonitorStateException的異常。
2. 執行wait, notify時,不獲得該物件的鎖會如何?
請看程式碼:
public static void main(String[] args) throws InterruptedException { Object obj = new Object(); Object lock = new Object(); synchronized (lock) { obj.wait(); obj.notifyAll(); } }
執行程式碼,同樣會丟擲java.lang.IllegalMonitorStateException的異常。
3. 為什麼在執行wait, notify時,必須獲得該物件的鎖?
這是因為,如果沒有鎖,wait和notify有可能會產生競態條件(Race Condition)。考慮以下生產者和消費者的情景:
1.1生產者檢查條件(如快取滿了)-> 1.2生產者必須等待
2.1消費者消費了一個單位的快取 -> 2.2重新設定了條件(如快取沒滿) -> 2.3呼叫notifyAll()喚醒生產者
我們希望的順序是: 1.1->1.2->2.1->2.2->2.3
但在多執行緒情況下,順序有可能是 1.1->2.1->2.2->2.3->1.2。也就是說,在生產者還沒wait之前,消費者就已經notifyAll了,這樣的話,生產者會一直等下去。
所以,要解決這個問題,必須在wait和notifyAll的時候,獲得該物件的鎖,以保證同步。
請看以下利用wait,notify實現的一個生產者、一個消費者和一個單位的快取的簡單模型:
public class QueueBuffer { int n; boolean valueSet = false; synchronized int get() { if (!valueSet) try { wait(); } catch (InterruptedException e) { System.out.println("InterruptedException caught"); } System.out.println("Got: " + n); valueSet = false; notify(); return n; } synchronized void put(int n) { if (valueSet) try { wait(); } catch (InterruptedException e) { System.out.println("InterruptedException caught"); } this.n = n; valueSet = true; System.out.println("Put: " + n); notify(); } }
public class Producer implements Runnable { private QueueBuffer q; Producer(QueueBuffer q) { this.q = q; new Thread(this, "Producer").start(); } public void run() { int i = 0; while (true) { q.put(i++); } } }
public class Consumer implements Runnable { private QueueBuffer q; Consumer(QueueBuffer q) { this.q = q; new Thread(this, "Consumer").start(); } public void run() { while (true) { q.get(); } } }
public class Main { public static void main(String[] args) { QueueBuffer q = new QueueBuffer(); new Producer(q); new Consumer(q); System.out.println("Press Control-C to stop."); } }
所以,JVM通過在執行的時候丟擲IllegalMonitorStateException的異常,來確保wait, notify時,獲得了物件的鎖,從而消除隱藏的Race Condition。
最後來看看一道題:寫一個多執行緒程式,交替輸出1,2,1,2,1,2……
利用wait, notify解決:
public class OutputThread implements Runnable { private int num; private Object lock; public OutputThread(int num, Object lock) { super(); this.num = num; this.lock = lock; } public void run() { try { while(true){ synchronized(lock){ lock.notifyAll(); lock.wait(); System.out.println(num); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args){ final Object lock = new Object(); Thread thread1 = new Thread(new OutputThread(1,lock)); Thread thread2 = new Thread(new OutputThread(2, lock)); thread1.start(); thread2.start(); } }
相關文章
- wait()和notify()、notifyAll()AI
- 如何在 Java 中正確使用 wait, notify 和 notifyAllJavaAI
- Java-併發-wait()、notify()和notifyAll()JavaAI
- wait、notify和notifyAll的關係AI
- sleep & wait | notify | notifyAllAI
- Java多執行緒8:wait()和notify()/notifyAll()Java執行緒AI
- Java常用的三個方法 `wait ` `notify` `notifyAll`JavaAI
- wait/notify/notifyAll 總結AI
- 條件佇列大法好:使用wait、notify和notifyAll的正確姿勢佇列AI
- 執行緒間協作——wait、notify、notifyAll執行緒AI
- 執行緒間的同步與通訊(2)——wait, notify, notifyAll執行緒AI
- Java 中的 Wait 和 Notify 機制JavaAI
- 【Java】【多執行緒】兩個執行緒間的通訊、wait、notify、notifyAllJava執行緒AI
- Java多執行緒 -- wait() 和 notify() 使用入門Java執行緒AI
- Java多執行緒的wait()和notify()例子Java執行緒AI
- java多執行緒 wait() notify()簡單使用Java執行緒AI
- wait() and notify()AI
- Java同步機制:synchronized,wait,notifyJavasynchronizedAI
- Java 非同步程式設計之:notify 和 wait 用法Java非同步程式設計AI
- 併發程式設計——執行緒中sleep(),yield(),join(),wait(),notify(),notifyAll()區別程式設計執行緒AI
- java多執行緒wait notify joinJava執行緒AI
- java基礎知識回顧之java Thread類學習(七)--java多執行緒通訊等待喚醒機制(wait和notify,notifyAll)...Javathread執行緒AI
- Java多執行緒中wait 和 notify 方法理解Java執行緒AI
- thread的notify和wait怎麼玩?threadAI
- Java通過wait()和notifyAll()方法實現執行緒間的通訊JavaAI執行緒
- 多執行緒(一)、基礎概念及notify()和wait()的使用執行緒AI
- 併發程式設計之Wait和Notify程式設計AI
- java併發程式設計系列:wait/notify機制Java程式設計AI
- Java併發(二十一)----wait notify介紹JavaAI
- Java多執行緒中的wait/notify通訊模式Java執行緒AI模式
- 深入執行緒的wait()/notify()執行緒AI
- Java併發(二十二)----wait notify的正確姿勢JavaAI
- 一個理解wait()與notify()的例子AI
- 記錄一段 Object wait()、notifyAll() 方法不當使用的經歷ObjectAI
- 條件佇列大法好:wait和notify的基本語義佇列AI
- wait和notify在鎖競爭中的執行順序AI
- 多執行緒中的wait與notify執行緒AI
- 一個理解wait()與notify()的例子 (轉)AI