Java的Object類包含了三個final方法,允許線程就資源的鎖定狀態進行通信。這三個方法分別是:wait(),notify(),notifyAll(),今天來了解一下這三個方法。在任何對象上調用這些方法的當前線程應具有對象監視器(鎖住了一個對象,就是獲得對象相關聯的監視器),否則會拋出java.lang.IllegalMonitorStateException異常。
wait
Object.wait有三種重載的實現,一個無限期等待任何其他線程地調用對象的notify或notifyAll方法來喚醒當前線程。 其他兩個會使當前線程在等待特定的時間后進行喚醒。
wait()
使得當前線程進程等待,直到另一個線程在這個對象上調用了notify()方法或者notifyAll()方法。這個方法的行為,完全等價於調用wait(0),可以看它的實現代碼為:
public final void wait() throws InterruptedException { wait(0); }
當前的線程必須獲得這個對象的監視器。線程釋放了監視器的所有權,直到另一個線程調用notify方法或者notifyAll方法,去喚醒在這個對象監視器上等待的其他線程。釋放了監視器的所有權后,線程便進行等待,直到重新獲得監視器的所有權並恢復執行。處於wait狀態中的線程,可能被中斷或虛假喚醒,所以這個方法應該總是在一個循環中使用:
synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition }
虛假喚醒指的是一些obj.wait()會在除了obj.notify()和obj.notifyAll()的其他情況被喚醒,而此時是不應該喚醒的,更詳細的可以看Spurious_wakeup。
方法會拋出兩種異常:
IllegalMonitorStateException:如果當前線程沒有獲得當前對象的監視器。
InterruptedException:如果某個線程在當前線程等待通知的時候,或是在等待通知之前中斷了當前線程,當拋出這個異常時,當前線程的中斷狀態被清除。
wait(long timeout)
該方法會使得當前進程進入wait狀態直到另一個線程調用notify/notifyAll,或是經過了指定的時間,線程將被喚醒。該方法會拋出異常:
IllegalArgumentException:如果timeout是負數。
wait(long timeout, int nanos)
同樣的,該方法會使得當前進程進入wait狀態直到另一個線程調用notify/notifyAll。它可以更加精細地控制等待的時間,以納秒為單位測量的實時量由下式給出:1000000*timeout+nanos。除此之外,這個方法與wait(long)做相同的事情,特別的,wait(0,0)等價於wait(0)。除了其余兩個wait方法會拋出的異常外,這個方法會拋出異常:
IllegalArgumentException:如果timeout是負數,或者nanos的范圍不在0-999999之間時,拋出該異常。
notify
notify方法只喚醒等待對象的一個線程,並且該線程開始執行。所以如果有多個線程在等待一個對象,這個方法只會喚醒其中的一個。線程的選擇取決於線程管理的OS實現。
notifyAll
notifyAll方法喚醒等待對象的所有線程,但哪一個將首先處理取決於操作系統的實現。
這些方法可用於實現生產者消費者問題,其中消費者線程正在等待隊列中的對象,生產者線程將對象放入隊列中並通知等待的線程。下面是一個多個線程工作在同一個對象上的例子,使用了wait,notify,notifyAll方法:
一個例子
Message :被通知喚醒的對象
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; } }
Notifier:執行喚醒操作
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(); } } }
Waiter: 使Message 對象進行wait狀態
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()); //process the message now System.out.println(name+" processed: "+msg.getMsg()); } } }
WaitNotifyTest:測試
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"); } }
輸出:
waiter waiting to get notified at time:1516757290631 All the threads are started waiter1 waiting to get notified at time:1516757290632 notifier started waiter waiter thread got notified at time:1516757291632 waiter processed: notifier Notifier work done
可以看到兩個線程在對象msg上進行等待,調用notify方法時,只有一個線程被喚醒,此時程序並沒有退出,因為還有一個線程在等待。
如果把notify方法改成notifyAll,運行結果為:
waiter waiting to get notified at time:1516757437164 waiter1 waiting to get notified at time:1516757437164 All the threads are started notifier started waiter1 waiter thread got notified at time:1516757438165 waiter1 processed: notifier Notifier work done waiter waiter thread got notified at time:1516757438165 waiter processed: notifier Notifier work done
可以看到兩個線程都被喚醒了,程序也退出運行了。
(完)
