JAVA多線程-Object.wait(),Object.notify(),Object.notifyAll()

初步理解

wait()

  1. 使線程停止運行,進入等待隊列,會立刻釋放對象鎖
  2. wait(0) 代表的是無限期的等待,不能自動喚醒。
  3. java.lang.Object類方法。

notify()

  1. 喚醒一個正在等待當前對象的waiting線程,遵循FIFO(先進先出)策略。
  2. notify()調用後,並不是馬上就釋放對象鎖,而是在相應的synchronized{…}語句塊執行結束後,自動釋放鎖後,JVM在wait()對象鎖的線程中隨機選取一線程賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線間同步、喚醒的操作
  3. java.lang.Object類方法。

notifyAll()

  1. 喚醒正在等待當前對象的所有等待線程。
  2. 默認的喚醒策略是:LIFO(後進先出)。
  3. java.lang.Object類方法。

wait()與notify()

  1. Obj.wait(),與Obj.notify()必須與synchronized(Obj)一起使用,也就是wait()是針對已經獲取了Obj鎖進行操作。
  2. 語法角度來說就是Obj.wait(),Obj.notify()必須在、synchronized(Obj){…}語句塊內。
  3. 功能上來說wait就是指在該線程獲取對象鎖後,主動釋放對象鎖,同時本線程休眠。notify()就是對對象鎖的喚醒操作。

wait()與sleep()
共同點

  1. 都可以指定線程阻塞的時間。
  2. 都可以通過interrupt()方法打斷(不建議使用)線程的暫停狀態 ,從而使線程立刻拋出InterruptedException。如果線程A希望立即結束線程B,則可以對線程B對應的Thread實例調用interrupt方法。如果此刻線程B正在wait/sleep /join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結束線程。
    需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用 interrupt()時,如果該線程正在執行普通的代碼,那麼該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join()後,就會立刻拋出InterruptedException 。

不同點

  1. sleep()是Thread類的方法,wait()是Object類的方法。
  2. sleep()方法沒有釋放鎖,而wait()方法釋放了鎖。
  3. wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用。

執行notify()後,waiting線程可能的狀態

線程a先搶到了對象o的鎖,然後wait,然後b搶到了o的鎖,然後b中調用o.notify並釋放鎖,此時a是running狀態還是blocked狀態??
如果b在執行完notify()後沒有釋放鎖則線程a是阻塞等待,
如果線程b執行完同步代碼塊(釋放鎖)後,則線程a就是就緒態,不一定是運行態

代碼實操,三線程打印ABC

建立三個線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時運行,交替打印10次ABC。這個問題用Object的wait(),notify()就可以很方便的解決。

public class T08WaitNotify {

    class T implements Runnable{
        // 自定義線程名稱
        private String name;
        // 前一個obj
        private Object prev;
        // 當前obj
        private Object self;

        T(String name, Object prev, Object self){
            this.name = name;
            this.prev = prev;
            this.self = self;
        }

        @Override
        public void run() {
            int count = 0;
            while (count < 10) {
                synchronized (prev) {
                    synchronized (self) {
                        System.out.print(name);
                        count++;
                        self.notify();
                    }
                    try {
                        prev.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        T08WaitNotify obj = new T08WaitNotify();

        Object oa = new Object();
        Object ob = new Object();
        Object oc = new Object();

        T a = obj.new T("A", oc, oa);
        T b = obj.new T("B", oa, ob);
        T c = obj.new T("C", ob, oc);

        Thread ta = new Thread(a);
        Thread tb = new Thread(b);
        Thread tc = new Thread(c);

        ta.start();
        Thread.sleep(1000); // 確保按照A、B、C順序執行
        tb.start();
        Thread.sleep(1000);
        tc.start();
        Thread.sleep(1000);
    }
}

代碼流程說明
A線程啓動,依次取得c、a鎖,打印A,執行notify()喚醒任意一個a對象等待池中的線程,待synchronized(self){}塊執行完後釋放a鎖,執行wait(),將A線程放入c對象相關的等待池中等待被喚醒同時釋放c鎖; 1秒後,B線程啓動,依次取得a、b鎖,打印B,執行notify()喚醒任意一個b對象等待池中的線程,待synchronized(self){}塊執行完後釋放b鎖,執行wait(),將B線程放入a對象相關的等待池中等待被喚醒,同時釋放a鎖;1秒後,C線程啓動,依次取得b、c鎖,打印C,執行notify()喚醒任意一個c對象等待池中的線程(此時A線程被喚醒),待synchronized(self){}塊執行完後釋放c鎖,執行wait(),將C線程放入b對象相關的等待池中等待被喚醒,同時釋放b鎖。

至此,完成一次ABC的打印。繼續執行,從A線程開始,執行各自線程中的下一次循環。

特別說明
Java源碼中wait()默認調用的是wait(0),表明線程無限等待,只能被其他線程喚醒。

參考
https://blog.csdn.net/u010002184/article/details/82912225
https://blog.csdn.net/hj1997a/article/details/84284973

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章