java 中的 Object 類別包含三個 final 方法,允許線程間通訊關於資源的鎖定狀態。這些方法是 wait()、notify() 和 notifyAll()。因此今天我們將研究在 Java 程式中的 wait、notify 和 notifyAll。
在 Java 中的 wait、notify 和 notifyAll
在任何物件上調用這些方法的當前線程應該擁有該物件的 monitor,否則將拋出 java.lang.IllegalMonitorStateException 例外。
wait
Object wait 方法有三種變化,一種是無限期等待其他線程調用該物件的 notify 或 notifyAll 方法以喚醒當前線程。另外兩種變體讓當前線程在特定時間內等待,然後再喚醒。
notify
notify()方法只喚醒一個在該物件上等待的線程,並且該線程開始執行。所以如果有多個線程在等待該物件,這個方法只會喚醒其中一個。喚醒的線程的選擇取決於操作系統對線程管理的實現。
notifyAll
notifyAll方法會喚醒在該物件上等待的所有線程,儘管哪個線程首先處理取決於操作系統的實現。這些方法可以用於實現生產者消費者問題,其中消費者線程在隊列中等待物件,生產者線程將物件放入隊列並通知等待的線程。讓我們看一個例子,其中多個線程在同一個物件上工作,我們使用wait、notify和notifyAll方法。
Message
A java bean class on which threads will work and call wait and notify methods.
package com.journaldev.concurrency;
public class Message {
private String msg;
public Message(String str){
this.msg=str;
}
public String getMsg() {
return msg;
}
public void setMsg(String str) {
this.msg=str;
}
}
Waiter
A class that will wait for other threads to invoke notify methods to complete it’s processing. Notice that Waiter thread is owning monitor on Message object using synchronized block.
package com.journaldev.concurrency;
public class Waiter implements Runnable{
private Message msg;
public Waiter(Message m){
this.msg=m;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (msg) {
try{
System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
msg.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
//現在處理消息
System.out.println(name+" processed: "+msg.getMsg());
}
}
}
通知者
A class that will process on Message object and then invoke notify method to wake up threads waiting for Message object. Notice that synchronized block is used to own the monitor of Message object.
package com.journaldev.concurrency;
public class Notifier implements Runnable {
private Message msg;
public Notifier(Message msg) {
this.msg = msg;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+" started");
try {
Thread.sleep(1000);
synchronized (msg) {
msg.setMsg(name+" Notifier work done");
msg.notify();
// msg.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
WaitNotifyTest
測試類別,將建立多個 Waiter 和 Notifier 線程並啟動它們。
package com.journaldev.concurrency;
public class WaitNotifyTest {
public static void main(String[] args) {
Message msg = new Message("process it");
Waiter waiter = new Waiter(msg);
new Thread(waiter,"waiter").start();
Waiter waiter1 = new Waiter(msg);
new Thread(waiter1, "waiter1").start();
Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifier").start();
System.out.println("All the threads are started");
}
}
當我們調用上面的程式時,將看到以下輸出,但程式不會完成,因為有兩個線程正在等待 Message 物件,而 notify() 方法只喚醒其中一個,另一個線程仍在等待通知。
waiter waiting to get notified at time:1356318734009
waiter1 waiting to get notified at time:1356318734010
All the threads are started
notifier started
waiter waiter thread got notified at time:1356318735011
waiter processed: notifier Notifier work done
如果我們將 Notifier 類別中的 notify() 呼叫註釋掉並取消註釋 notifyAll() 呼叫,將產生以下輸出。
waiter waiting to get notified at time:1356318917118
waiter1 waiting to get notified at time:1356318917118
All the threads are started
notifier started
waiter1 waiter thread got notified at time:1356318918120
waiter1 processed: notifier Notifier work done
waiter waiter thread got notified at time:1356318918120
waiter processed: notifier Notifier work done
由於 notifyAll() 方法會喚醒兩個 Waiter 線程,程式在執行後完成並終止。這就是 Java 中等待、通知和 notifyAll 的全部。
Source:
https://www.digitalocean.com/community/tutorials/java-thread-wait-notify-and-notifyall-example