wait/notify/notifyAll 總結
概述
在Java中,可以通過配合呼叫Object物件的wait()方法和notify()方法或notifyAll()方法來實現執行緒間的通訊。線上程中呼叫wait()方法,將阻塞,然後等待其他執行緒通過呼叫notify()方法或notifyAll()發起的通知,線上程中呼叫notify()方法或notifyAll()方法,將通知其他執行緒從wait()方法處返回。
Object是所有類的超類,它有5個方法組成了等待/通知機制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。在Java中,所有的類都從Object繼承而來,因此,所有的類都擁有這些共有方法可供使用。而且,由於他們都被宣告為final,因此在子類中不能覆寫任何一個方法。
wait()
public final void wait()
throws InterruptedException,IllegalMonitorStateException
該方法用來將當前執行緒置入休眠狀態,直到接到通知或被中斷為止。在呼叫wait()之前,執行緒必須要獲得該物件的物件級別鎖,即只能在同步方法或同步塊中呼叫wait()方法。進入wait()方法後,當前執行緒釋放鎖。在從wait()返回前,執行緒與其他執行緒競爭重新獲得鎖。如果呼叫wait()時,沒有持有適當的鎖,則丟擲IllegalMonitorStateException,它是RuntimeException的一個子類,因此,不需要try-catch結構。
notify()
public final native void notify()
throws IllegalMonitorStateException
該方法也要在同步方法或同步塊中呼叫,即在呼叫前,執行緒也必須要獲得該物件的物件級別鎖,的如果呼叫notify()時沒有持有適當的鎖,也會丟擲IllegalMonitorStateException。
該方法用來通知那些可能等待該物件的物件鎖的其他執行緒。如果有多個執行緒等待,則執行緒規劃器任意挑選出其中一個wait()狀態的執行緒來發出通知,並使它等待獲取該物件的物件鎖(notify後,當前執行緒不會馬上釋放該物件鎖,wait所在的執行緒並不能馬上獲取該物件鎖,要等到程式退出synchronized程式碼塊後,當前執行緒才會釋放鎖,wait所在的執行緒也才可以獲取該物件鎖),但不驚動其他同樣在等待被該物件notify的執行緒們。當第一個獲得了該物件鎖的wait執行緒執行完畢以後,它會釋放掉該物件鎖,此時如果該物件沒有再次使用notify語句,則即便該物件已經空閒,其他wait狀態等待的執行緒由於沒有得到該物件的通知,會繼續阻塞在wait狀態,直到這個物件發出一個notify或notifyAll。這裡需要注意:它們等待的是被notify或notifyAll,而不是鎖。這與下面的notifyAll()方法執行後的情況不同。
notifyAll()
public final native void notifyAll()
throws IllegalMonitorStateException
該方法與notify()方法的工作方式相同,重要的一點差異是:
notifyAll使所有原來在該物件上wait的執行緒統統退出wait的狀態(即全部被喚醒,不再等待notify或notifyAll,但由於此時還沒有獲取到該物件鎖,因此還不能繼續往下執行),變成等待獲取該物件上的鎖,一旦該物件鎖被釋放(notifyAll執行緒退出呼叫了notifyAll的synchronized程式碼塊的時候),他們就會去競爭。如果其中一個執行緒獲得了該物件鎖,它就會繼續往下執行,在它退出synchronized程式碼塊,釋放鎖後,其他的已經被喚醒的執行緒將會繼續競爭獲取該鎖,一直進行下去,直到所有被喚醒的執行緒都執行完畢(與 notify 不同的點)。
wait(long)和wait(long,int)
顯然,這兩個方法是設定等待超時時間的,後者在超值時間上加上ns,精度也難以達到,因此,該方法很少使用。對於前者,如果在等待執行緒接到通知或被中斷之前,已經超過了指定的毫秒數,則它通過競爭重新獲得鎖,並從wait(long)返回。另外,需要知道,如果設定了超時時間,當wait()返回時,我們不能確定它是因為接到了通知還是因為超時而返回的,因為wait()方法不會返回任何相關的資訊。但一般可以通過設定標誌位來判斷,在notify之前改變標誌位的值,在wait()方法後讀取該標誌位的值來判斷,當然為了保證notify不被遺漏,我們還需要另外一個標誌位來迴圈判斷是否呼叫wait()方法。
消費者、生產者
生產者-消費者(producer-consumer)問題,也稱作有界緩衝區(bounded-buffer)問題,兩個程式共享一個公共的固定大小的緩衝區。其中一個是生產者,用於將訊息放入緩衝區;另外一個是消費者,用於從緩衝區中取出訊息。問題出現在當緩衝區已經滿了,而此時生產者還想向其中放入一個新的資料項的情形,其解決方法是讓生產者此時進行休眠,等待消費者從緩衝區中取走了一個或者多個資料後再去喚醒它。同樣地,當緩衝區已經空了,而消費者還想去取訊息,此時也可以讓消費者進行休眠,等待生產者放入一個或者多個資料時再喚醒它。
public class ProducerConsumer {
private LinkedList<Object> storeHouse = new LinkedList<Object>();
private int MAX = 10;
public ProducerConsumer() {
}
public void start() {
new Producer().start();
new Comsumer().start();
}
class Producer extends Thread {
public void run() {
while (true) {
synchronized (storeHouse) {
try {
while (storeHouse.size() == MAX) {
System.out.println("storeHouse is full , please wait");
storeHouse.wait();
}
Object newOb = new Object();
if (storeHouse.add(newOb)) {
System.out.println("Producer put a Object to storeHouse");
Thread.sleep((long) (Math.random() * 3000));
storeHouse.notify();
}
} catch (InterruptedException ie) {
System.out.println("producer is interrupted!");
}
}
}
}
}
class Comsumer extends Thread {
public void run() {
while (true) {
synchronized (storeHouse) {
try {
while (storeHouse.size() == 0) {
System.out.println("storeHouse is empty , please wait");
storeHouse.wait();
}
storeHouse.removeLast();
System.out.println("Comsumer get a Object from storeHouse");
Thread.sleep((long) (Math.random() * 3000));
storeHouse.notify();
} catch (InterruptedException ie) {
System.out.println("Consumer is interrupted");
}
}
}
}
}
public static void main(String[] args) throws Exception {
ProducerConsumer pc = new ProducerConsumer();
pc.start();
}
}
總結
- 如果執行緒呼叫了物件的wait()方法,那麼執行緒便會處於該物件的等待池中,等待池中的執行緒不會去競爭該物件的鎖。
- 當有執行緒呼叫了物件的notifyAll()方法(喚醒所有wait執行緒)或notify()方法(只隨機喚醒一個wait執行緒),被喚醒的的執行緒便會進入該物件的鎖池中,鎖池中的執行緒會去競爭該物件鎖。
- 優先順序高的執行緒競爭到物件鎖的概率大,假若某執行緒沒有競爭到該物件鎖,它還會留在鎖池中,唯有執行緒再次呼叫wait()方法,它才會重新回到等待池中。而競爭到物件鎖的執行緒則繼續往下執行,直到執行完了synchronized程式碼塊,它會釋放掉該物件鎖,這時鎖池中的執行緒會繼續競爭該物件鎖。
參考
相關文章
- wait()和notify()、notifyAll()AI
- sleep & wait | notify | notifyAllAI
- wait、notify和notifyAll的關係AI
- Java-併發-wait()、notify()和notifyAll()JavaAI
- 執行緒間協作——wait、notify、notifyAll執行緒AI
- Java的wait(), notify()和notifyAll()使用心得JavaAI
- Java常用的三個方法 `wait ` `notify` `notifyAll`JavaAI
- Java多執行緒8:wait()和notify()/notifyAll()Java執行緒AI
- 如何在 Java 中正確使用 wait, notify 和 notifyAllJavaAI
- 執行緒間的同步與通訊(2)——wait, notify, notifyAll執行緒AI
- wait() and notify()AI
- 條件佇列大法好:使用wait、notify和notifyAll的正確姿勢佇列AI
- 併發程式設計——執行緒中sleep(),yield(),join(),wait(),notify(),notifyAll()區別程式設計執行緒AI
- 【Java】【多執行緒】兩個執行緒間的通訊、wait、notify、notifyAllJava執行緒AI
- Java同步機制:synchronized,wait,notifyJavasynchronizedAI
- 深入執行緒的wait()/notify()執行緒AI
- Java 中的 Wait 和 Notify 機制JavaAI
- java多執行緒wait notify joinJava執行緒AI
- 一個理解wait()與notify()的例子AI
- 併發程式設計之Wait和Notify程式設計AI
- 併發-6-wait、notify、Semaphore、CountDownLatch、CyclicBarrierAICountDownLatch
- 多執行緒中的wait與notify執行緒AI
- thread的notify和wait怎麼玩?threadAI
- 一個理解wait()與notify()的例子 (轉)AI
- 併發程式設計之 wait notify 方法剖析程式設計AI
- java併發程式設計系列:wait/notify機制Java程式設計AI
- 執行緒篇2:[- sleep、wait、notify、join、yield -]執行緒AI
- java多執行緒 wait() notify()簡單使用Java執行緒AI
- Java併發(二十一)----wait notify介紹JavaAI
- Java多執行緒的wait()和notify()例子Java執行緒AI
- JVM原始碼分析之Object.wait/notify實現JVM原始碼ObjectAI
- Java 非同步程式設計之:notify 和 wait 用法Java非同步程式設計AI
- Java多執行緒 -- wait() 和 notify() 使用入門Java執行緒AI
- java基礎知識回顧之java Thread類學習(七)--java多執行緒通訊等待喚醒機制(wait和notify,notifyAll)...Javathread執行緒AI
- 【Java】sleep和wait區別總結JavaAI
- 使用wait()與notifyAll()實現生產者與消費者模式AI模式
- 啃碎併發(八):深入分析wait¬ify原理AI
- Java多執行緒中wait 和 notify 方法理解Java執行緒AI