java多線程系列基礎篇05-----線程等待與喚醒

wait(),notify(),notifyAll()等方法介紹

在Object.java中,定義了wait(),notify(),和notifyAll()等接口。wait()的作用是讓當前線程進入等待狀態,同時,wait()也會讓當前線程釋放它所持有的鎖。而notify()和notifyAll()的作用,則是喚醒當前對象上的等待線程,notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程

Object類中關於等待/喚醒的API詳細信息如下

notify() 喚醒在此對象監視器上等待的單個線程

notifyAll()喚醒在此對象監視器上等待的所有線程

wait()   讓當前線程處於等待(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,當前線程被喚醒(進入"就緒狀態")

wait(long timeout)讓當前線程處於等待(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,當前線程被喚醒(進入"就緒狀態")或者超過我們指定的時間量 當前線程被喚醒(進入就緒狀態)

wait(long timeout,int nanos)讓當前線程處於等待(阻塞)狀態,直到其他線程調用此對象的notify()方法或notifyAll()方法,當前線程被喚醒(進入"就緒狀態")或者超過我們指定的時間量 或者其他線程中斷當前線程 當前線程被喚醒(進入就緒狀態)

2 wait()和notify()示例

package com.tuhu.filt.javadatathread;

public class ThreadA extends Thread{
    public ThreadA(String name){
        super(name);
    }
    public void run(){
        synchronized (this){
            System.out.println(
                    Thread.currentThread().getName()
                    +" call notify"
            );
            notify();
        }
    }
}
class WaitTest{
    public static void main(String[] args) {
        ThreadA t1 = new ThreadA("t1");
        synchronized (t1){
            try{
                System.out.println(
                        Thread.currentThread().getName()
                                +" start t1"
                );
                t1.start();
                /**
                 * 下面這個輸出語句也是main線程調用的,調用了之後執行run方法才
                 * 出現那個t1 也就是t0
                 */
                System.out.println(
                        Thread.currentThread().getName()
                        +" wait()"
                );
                t1.wait();
                /**
                 * t1阻塞之後 又把main給喚醒了 哈哈哈
                 */
                System.out.println(
                        Thread.currentThread().getName()
                        +" continue"
                );
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        }
    }
}

我們來說明下主線程和線程t1的流程

 

3 wait(long timeout)和notify()

下面演示wait(long timeout)在超時情況下,線程被喚醒的情況

package com.tuhu.filt.javadatathread;

public class ThreadB extends Thread {
    public ThreadB(String name){
        super(name);
    }
    public void run(){
        System.out.println(Thread.currentThread().getName()
        +" run"
        );
        while(true){
            ;
        }
    }
}
class WaitTimeoutTest{
    public static void main(String[] args) {
        ThreadB t1 = new ThreadB("t1");
        synchronized (t1){
            try{
                System.out.println(
                        Thread.currentThread().getName()
                        +" start t1"
                );
                t1.start();
                System.out.println(
                        Thread.currentThread().getName()
                        +" call wait"
                );
                t1.wait(3000);
                System.out.println(
                        Thread.currentThread().getName()
                        +" continue"
                );
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

4 wait()和notifyAll()

public class NotifyAllTest {

    private static Object obj = new Object();
    public static void main(String[] args) {

        ThreadA t1 = new ThreadA("t1");
        ThreadA t2 = new ThreadA("t2");
        ThreadA t3 = new ThreadA("t3");
        t1.start();
        t2.start();
        t3.start();

        try {
            System.out.println(Thread.currentThread().getName()+" sleep(3000)");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized(obj) {
            // 主線程等待喚醒。
            System.out.println(Thread.currentThread().getName()+" notifyAll()");
            obj.notifyAll();
        }
    }

    static class ThreadA extends Thread{

        public ThreadA(String name){
            super(name);
        }

        public void run() {
            synchronized (obj) {
                try {
                    // 打印輸出結果
                    System.out.println(Thread.currentThread().getName() + " wait");

                    // 喚醒當前的wait線程
                    obj.wait();

                    // 打印輸出結果
                    System.out.println(Thread.currentThread().getName() + " continue");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue

結果說明
參考下面的流程圖。 
(01) 主線程中新建並且啓動了3個線程"t1", "t2"和"t3"。
(02) 主線程通過sleep(3000)休眠3秒。在主線程休眠3秒的過程中,我們假設"t1", "t2"和"t3"這3個線程都運行了。以"t1"爲例,當它運行的時候,它會執行obj.wait()等待其它線程通過notify()或額nofityAll()來喚醒它;相同的道理,"t2"和"t3"也會等待其它線程通過nofity()或nofityAll()來喚醒它們。
(03) 主線程休眠3秒之後,接着運行。執行 obj.notifyAll() 喚醒obj上的等待線程,即喚醒"t1", "t2"和"t3"這3個線程。 緊接着,主線程的synchronized(obj)運行完畢之後,主線程釋放“obj鎖”。這樣,"t1", "t2"和"t3"就可以獲取“obj鎖”而繼續運行了!

5 爲什麼notify() wait()等函數定義在object而不是在Thread中

object wait() notify() 和synchronized一樣 會對對象同步鎖 進行操作

wait()會讓當前線程進入等待狀態 進入等待狀態  所以線程應該釋放它所持有的"同步鎖",否則其他線程獲取不到該"同步鎖"而無法運行

釋放掉同步鎖之後,等待線程可以被notify()喚醒

請思考一個問題:notify()是依據什麼喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什麼關聯起來的?答案是:依據“對象的同步鎖”。

負責喚醒等待線程的那個線程(我們稱爲“喚醒線程”),它只有在獲取“該對象的同步鎖”(這裏的同步鎖必須和等待線程的同步鎖是同一個),並且調用notify()或notifyAll()方法之後,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執行,因爲喚醒線程還持有“該對象的同步鎖”。必須等到喚醒線程釋放了“對象的同步鎖”之後,等待線程才能獲取到“對象的同步鎖”進而繼續運行。

總之,notify(), wait()依賴於“同步鎖”,而“同步鎖”是對象鎖持有,並且每個對象有且僅有一個!這就是爲什麼notify(), wait()等函數定義在Object類,而不是Thread類中的原因。

發佈了84 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章