多線程-等待(Wait)和通知(notify)


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()方法不會釋放任何資源。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM