文章目錄
- wait and notify(等待通知機制
- notify
- 補充
wait and notify(等待通知機制
引入wait notify就是為了能夠從應用層面上,干預到多個不同線程代碼的執行順序,這里說的干預,不是影響系統的線程調度策略(內核里調度線程,仍然是無序調度)
相當于是在應用程序代碼中,讓后執行的線程,主動放棄被調度的機會,就可以讓先執行的線程,先把對應代碼執行完了
舉例:
在ATM機上取錢
ATM通過鎖來互斥
第一個人,先進去,發現ATM沒錢,然后出來了,但是和其他人來競爭這個ATM,參與到了鎖競爭中,此時完全有可能第一個人再次拿到這個鎖,如果反復如此,就會導致第一個人反復獲取到鎖,但又無法完成實質性的邏輯,導致“線程餓死”。
這種情況就是嚴重的bug,當第一個人發現自己要執行的邏輯,前提條件不具備,在這種情況下,應該主動放棄對鎖的競爭,一直到條件具備,此時再解除阻塞,參與鎖競爭,這個時候就要用到wait和notify
join是等待另一個線程執行完,才繼續執行
wait則是等待另一個線程通過notify進行通知(不要求另一個線程必須執行完)
wait進入阻塞,只能說明自己釋放鎖了
其他線程是否拿到了鎖,另當別論
阻塞產生的原因有好幾種:
1.sleep TIMED_WAITING
2.join/wait WAITING
3.synchronized BLOCKED
Object object = new Object();object.wait();
隨便拿一個對象都能調用wait但是運行起來會報異常
不合法監視狀態異常
原因:wait一旦調用就會釋放鎖,釋放鎖的前提是拿到鎖,所以,wait必須放到synchronized里面使用。
wait鎖對象必須和synchronized鎖對象一致
public class Test4 {public static void main(String[] args) {Object object = new Object();Thread t1 = new Thread(()->{synchronized (object){System.out.println("wait前");try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait后");}});t1.start();}
}
輸出結果:
wait前
代碼一直沒有結束
在Java監視和管理控制臺上:
notify
鎖被wait就需要來喚醒鎖,notify就是來喚醒鎖的
notify可以不放在synchronized里面,但是Java規定notify必須放在synchronized里面
public class Test4 {public static void main(String[] args) throws InterruptedException {Object object = new Object();Thread t1 = new Thread(()->{synchronized (object){System.out.println("wait前");try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("wait后");}});Thread t2 = new Thread(()->{try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized(object){System.out.println("notify前");object.notify();System.out.println("notify后");}});t1.start();t2.start();}
}
運行結果:
wait前
notify前
notify后
wait后
線程2使用sleep的原因:
因為線程調度是隨機的,不確定線程1還是線程2先運行,如果線程2先運行就達不到我們預期的結果,所以sleep線程2,讓線程1先運行,先執行wait,再執行notify。
上述代碼運行執行過程:
1.t1先執行起來后,就會立即拿到鎖,并打印wait前,然后進入wait方法(釋放鎖+阻塞等待)
2.t2執行起來,先進行sleep五秒(為了讓t1先拿到鎖)
3.t2sleep結束后,由于t1是wait狀態,鎖是釋放的,t2就能拿到鎖,接下來打印notify前,執行notify操作,喚醒t1
4.由于t2還沒有釋放鎖,t1想要獲取鎖,可能會出現阻塞,和t2鎖競爭導致
5.t2打印notify后,釋放鎖,t2執行完畢,t1獲取到鎖,打印wait后
補充