1.為了支撐多線程之間的協作,JDK提供了兩個非常重要的線程接口:等待wait()方法和通知notify()方法。
這兩個方法並不是在Thread類中的,而是輸出在Object類。這意味着任何對象都可以調用這兩個方法。
這兩個方法如下
當在一個對象實例上調用wait()方法后,當前線程就會在這個對象上等待。
比如,在線程A中,調用了obj.wait()方法,那么線程A就會停止繼續執行,轉為等待狀態。等待到何時結束呢?線程A會一直等到其他線程調用了obj.notity()方法為止。
2.wait()方法和notify()方法究竟是如何工作的呢?
如果一個線程調用了object.wait()方法,那么它就會進入object對象的等待隊列,這個隊列中,可能會有多個線程,因為系統運行多個線程同時等待某一個對象,
當object.notify()方法被調用的時候,它就會從這個等待隊列中隨機選擇一個線程,並進行喚醒。
除notity()方法外,Object對象還有一個類似的notifyAll()方法,它和notity方法的功能基本一致,不同的是,它會喚醒在這個等待隊列中所有等待的線程,而不是隨機一個。
object.wait()方法並不能隨便調用。它必須包含在對象的synchronzied語句中,無論是wait()方法或者notity()方法都需要首先獲得目標對象的一個監視器。
如圖,其中T1和T2表示兩個線程,T1在正確執行wait()方法前,必須獲得object對象的監視器,而wait()方法執行之后會釋放這個監視器。
這樣做的目的是使其他等待在object對象上的線程不至於因為T1的休眠而全部無法正常執行。
線程T2在notity()方法調用前,也必須獲得object對象的監視器。此時T1已經釋放了這個監視器。所以T2可以順利獲得object對象的監視器。
接着,T2執行了notify()方法嘗試喚醒一個等待線程,這里假設喚醒了T1,T1被喚醒后,要做的第一件事並不是執行后續代碼,而是要嘗試重新
獲得object對象的監視器,而這個監視器也正是T1在wait()方法執行前所持有的那個。
如果暫時無法獲得,則T1還必須等待這個監視器。當監視器順利獲得后,T1才可以在真正意義上繼續執行。
3.為了方便理解,簡單的案例:
/** * wait和 notify 的學習 * @author wm * @date 2019/9/18 15:15 */ public class SimpleWN { final static Object object = new Object(); public static class T1 extends Thread{ public void run() { synchronized (object){ System.out.println(System.currentTimeMillis()+":T1 start! "); System.out.println(System.currentTimeMillis()+":T1 wait for object"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T1 end! "); } } } public static class T2 extends Thread{ public void run() { synchronized (object){ System.out.println(System.currentTimeMillis()+":T2 start! notify one thread"); object.notify(); System.out.println(System.currentTimeMillis()+":T2 end! "); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread t1 = new T1(); Thread t2 = new T2(); t1.start(); t2.start(); } }
上述代碼中,開啟了T1和T2兩個線程。T1執行了object.wait()方法。執行wait()方法前,T1先申請object的對象鎖。
因此,在執行object.wait()時,它持有的是object的對象鎖的。wait()方法執行后,T1會進行等待,並釋放object對象鎖。
T2在執行notity()方法之前也會先獲得object的對象鎖,這里為了讓結果明顯,特意在notity()方法執行之后,讓T2休眠2秒,
這樣做可以更明顯地說明,T1在得到notity()方法通知后,還是會先嘗試重新獲得object的對象鎖。
執行結果:
1570675715571:T1 start! 1570675715571:T1 wait for object 1570675715571:T2 start! notify one thread 1570675715571:T2 end! 1570675715571:T1 end!
在T2通知T1繼續執行后,T1並不能立即繼續執行,而是要等待T2釋放object的鎖,並重新成功獲得鎖后,才能繼續執行;
4.Object.wait()方法和Thread.sleep()方法都可以讓線程等待若干時間,除wait()方法可以被喚醒外,
另外一個主要區別就是wait()方法會釋放目標對象的鎖,而Thread.sleep()方法不會釋放任何資源。