1. 程式人生 > >【多執行緒】wait和notify功能演示

【多執行緒】wait和notify功能演示

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()示例

下面通過示例演示"wait()和notify()配合使用的情形"。

// WaitTest.java的原始碼
class ThreadA extends Thread{

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

    public void run() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName()+" call notify()");
            // 喚醒當前的wait執行緒
            notify();
        }
    }
}

public class WaitTest {

    public static void main(String[] args) {

        ThreadA t1 = new ThreadA("t1");

        synchronized(t1) {
            try {
                // 啟動“執行緒t1”
                System.out.println(Thread.currentThread().getName()+" start t1");
                t1.start();

                // 主執行緒等待t1通過notify()喚醒。
                System.out.println(Thread.currentThread().getName()+" wait()");
                t1.wait();

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

執行結果:

main start t1
main wait()
t1 call notify()
main continue

結果說明
如下圖,說明了“主執行緒”和“執行緒t1”的流程。

(01) 注意,圖中"主執行緒" 代表“主執行緒main”。"執行緒t1" 代表WaitTest中啟動的“執行緒t1”。 而“鎖” 代表“t1這個物件的同步鎖”。
(02) “主執行緒”通過 new ThreadA("t1") 新建“執行緒t1”。隨後通過synchronized(t1)獲取“t1物件的同步鎖”。然後呼叫t1.start()啟動“執行緒t1”。
(03) “主執行緒”執行t1.wait() 釋放“t1物件的鎖”並且進入“等待(阻塞)狀態”。等待t1物件上的執行緒通過notify() 或 notifyAll()將其喚醒。
(04) “執行緒t1”執行之後,通過synchronized(this)獲取“當前物件的鎖”;接著呼叫notify()喚醒“當前物件上的等待執行緒”,也就是喚醒“主執行緒”。
(05) “執行緒t1”執行完畢之後,釋放“當前物件的鎖”。緊接著,“主執行緒”獲取“t1物件的鎖”,然後接著執行。

對於上面的程式碼?曾經有個朋友問到過:t1.wait()應該是讓“執行緒t1”等待;但是,為什麼卻是讓“主執行緒main”等待了呢?
在解答該問題前,我們先看看jdk文件中關於wait的一段介紹:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. 
In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

中文意思大概是:

引起“當前執行緒”等待,直到另外一個執行緒呼叫notify()或notifyAll()喚醒該執行緒。換句話說,這個方法和wait(0)的效果一樣!(補充,對於wait(long millis)方法,當millis為0時,表示無限等待,直到被notify()或notifyAll()喚醒)。
“當前執行緒”在呼叫wait()時,必須擁有該物件的同步鎖。該執行緒呼叫wait()之後,會釋放該鎖;然後一直等待直到“其它執行緒”呼叫物件的同步鎖的notify()或notifyAll()方法。然後,該執行緒繼續等待直到它重新獲取“該物件的同步鎖”,然後就可以接著執行。

注意:jdk的解釋中,說wait()的作用是讓“當前執行緒”等待,而“當前執行緒”是指正在cpu上執行的執行緒!
這也意味著,雖然t1.wait()是通過“執行緒t1”呼叫的wait()方法,但是呼叫t1.wait()的地方是在“主執行緒main”中。而主執行緒必須是“當前執行緒”,也就是執行狀態,才可以執行t1.wait()。所以,此時的“當前執行緒”是“主執行緒main”!因此,t1.wait()是讓“主執行緒”等待,而不是“執行緒t1”!

3. wait(long timeout)和notify()

wait(long timeout)會讓當前執行緒處於“等待(阻塞)狀態”,“直到其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量”,當前執行緒被喚醒(進入“就緒狀態”)。
下面的示例就是演示wait(long timeout)在超時情況下,執行緒被喚醒的情況。

// WaitTimeoutTest.java的原始碼
class ThreadA extends Thread{

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

    public void run() {
        System.out.println(Thread.currentThread().getName() + " run ");
        // 死迴圈,不斷執行。
        while(true)
            ;
    }
}

public class WaitTimeoutTest {

    public static void main(String[] args) {

        ThreadA t1 = new ThreadA("t1");

        synchronized(t1) {
            try {
                // 啟動“執行緒t1”
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();

                // 主執行緒等待t1通過notify()喚醒 或 notifyAll()喚醒,或超過3000ms延時;然後才被喚醒。
                System.out.println(Thread.currentThread().getName() + " call wait ");
                t1.wait(3000);

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

執行結果

main start t1
main call wait 
t1 run                  // 大約3秒之後...輸出“main continue”
main continue

結果說明
如下圖,說明了“主執行緒”和“執行緒t1”的流程。
(01) 注意,圖中"主執行緒" 代表WaitTimeoutTest主執行緒(即,執行緒main)。"執行緒t1" 代表WaitTest中啟動的執行緒t1。 而“鎖” 代表“t1這個物件的同步鎖”。
(02) 主執行緒main執行t1.start()啟動“執行緒t1”。
(03) 主執行緒main執行t1.wait(3000),此時,主執行緒進入“阻塞狀態”。需要“用於t1物件鎖的執行緒通過notify() 或者 notifyAll()將其喚醒” 或者 “超時3000ms之後”,主執行緒main才進入到“就緒狀態”,然後才可以執行。
(04) “執行緒t1”執行之後,進入了死迴圈,一直不斷的執行。
(05) 超時3000ms之後,主執行緒main會進入到“就緒狀態”,然後接著進入“執行狀態”。

4. wait() 和 notifyAll()

通過前面的示例,我們知道 notify() 可以喚醒在此物件監視器上等待的單個執行緒。
下面,我們通過示例演示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()會使“當前執行緒”等待,因為執行緒進入等待狀態,所以執行緒應該釋放它鎖持有的“同步鎖”,否則其它執行緒獲取不到該“同步鎖”而無法執行!
OK,執行緒呼叫wait()之後,會釋放它鎖持有的“同步鎖”;而且,根據前面的介紹,我們知道:等待執行緒可以被notify()或notifyAll()喚醒。現在,請思考一個問題:notify()是依據什麼喚醒等待執行緒的?或者說,wait()等待執行緒和notify()之間是通過什麼關聯起來的?答案是:依據“物件的同步鎖”。

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

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

相關推薦

執行waitnotify功能演示

wait(), notify(), notifyAll()等方法介紹 在Object.java中,定義了wait(), notify()和notifyAll()等介面。wait()的作用是讓當前執行緒進入等待狀態,同時,wait()也會讓當前執行緒釋放它所持有的鎖。

執行WaitJoin的理解

對於,wait方法的解釋,有時候覺得很矛盾。呼叫某個物件的wait時,需要獲得該物件的鎖,在執行的時候又需要釋放該物件的所有鎖。這是問題一。 另外,在看Join的原始碼,會發現它利用了Wait來實現,但是它的實現原理是怎樣的呢? 這是問題二。 看下原始碼的英文描述:

執行——join、yield、wait、sleep的區別

join    通常由使用執行緒的程式呼叫,將大問題劃分為許多小問題,每個小問題分配一個執行緒,當所有小問題都得到處理後,再呼叫主執行緒進一步操作。 join(); Join(long mi

執行執行通訊waitnotify的使用

使用wait和notify方法實現執行緒之間的通訊,注意,這兩個方法是Object類的方法,也就是說Java為所有的物件都提供的這兩個方法。 1 wait和notify必須配合synchronized關鍵字使用。 2 wait方法釋放鎖,notify方法不釋

執行執行

1、好處 第一:降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。 第二:提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。 第三:提高執行緒的可管理性。執行緒是稀缺資源,如果無限制地建立,不僅會消耗系統資源, 還會降低系統的穩定性,使用執行

執行ListenableFuture非同步執行查詢實現

  業務場景:為優化查詢效率,將原有查詢的條件做成單獨的索引表,每次產生記錄就會同步到索引表中,每次查詢索引表,根據索引便利的條件欄位再分別查詢每張子表的內容,最後封裝成前臺要的實體類。這裡面涉及到非同步查詢,如何保證一條記錄下的子表全部都查出來後才執行下面的操作。 下面Demo簡

執行在專案中用JAVA使用執行

一,初衷 因為在學習java基礎的時候,學習過兩種實現多執行緒的方法。今天在看一個文章的時候,看到了別人在專案中執行多執行緒。想到自己還沒用過,所以將別人的使用方法記錄下來,方便以後自己在專案中呼叫多執行緒可以嘗試一下。 二,多執行緒的實現 2.1 繼

執行Thread.interrupted()與thread.isInterrupted()的區別

在Java的執行緒基本操作方法中,有兩種方式獲取當前執行緒的isInterrupt屬性。一種是物件方法thread.isInterrupted(),另一種是Thread類的靜態方法Thread.interrupted()。這兩個方法看似相同,實際上是有區別的,我們來看看Jav

執行waitsleep區別

wiat和sleep的區別? 1、wait可以指定時間也可以不指定      sleep必須指定時間。 2、在同步中,對cpu的執行權和鎖的處理不同 wait:釋 放執行權,釋放鎖。 slee

執行通訊(waitnotify、Lock、ThreadLocal)

多執行緒之間通訊 什麼是多執行緒通訊? 就是多個執行緒對同一個共享資源,進行不同的操作。 介紹兩個API中的方法,這兩個是Object裡面的方法: wait();等待,執行緒從執行狀態變為休眠狀態 notify();喚醒,執行緒從休眠狀態變為執行狀態 現在解決一下這樣一個案例: 兩個執行緒,面向一個倉庫進行

《瘋狂Java講義(第4版)》-----第16章執行執行通訊、執行池)

執行緒通訊 傳統的執行緒通訊 用synchonized同步的情況下,可以使用Object的三個方法: wait():釋放同步監視器,直到其他執行緒呼叫該同步監視器的notify()或notifyAll()方法 notify():喚醒此同步監視器上等待的單個執行緒

《瘋狂Java講義(第4版)》-----第16章執行(控制執行執行同步)

控制執行緒 join執行緒 等那個執行緒做完後,當前執行緒再做! import java.lang.Thread; public class MyThread extends Thread{ public MyThread(String name){ super(

《瘋狂Java講義(第4版)》-----第16章執行執行的建立及生命週期)

執行緒的獨立執行的,他並不知道程序是否還有其他執行緒存在 當作業系統建立一個程序時,必須為該程序分配獨立的記憶體空間,並分配大量的相關資源;但建立一個執行緒則簡單得多,因此使用多執行緒來實現併發比使用多程序實現併發的效能要高得多 多執行緒是非常有用的,一個瀏覽器必須能

執行程式猿進階執行(四)—— 執行同步

一、前言       在上一篇部落格,小編向大家介紹了執行緒的狀態,算是進一步拉開了多執行緒的面試,在這篇部落格中,小編向大家介紹一下多執行緒中常見問題有執行緒同步和執行緒通訊,這篇部落格中小編向大家

Java執行CallableFuture

Future模式 Future介面是Java執行緒Future模式的實現,可以來進行非同步計算。 Future模式可以這樣來描述: 我有一個任務,提交給了Future,Future替我完成這個任務。期間我自己可以去做任何想做的事情。一段時間之後,我就便可以從Future那兒

執行BlockingQueue詳解

前言:      在新增的Concurrent包中,BlockingQueue很好的解決了多執行緒中,如何高效安全“傳輸”資料的問題。通過這些高效並且執行緒安全的佇列類,為我們快速搭建高質量的多執行緒程式帶來極大的便利。本文詳細介紹了BlockingQueue家庭中的所有成員

執行實現執行同步的幾種方法(一)

前言 最近小扁我被問到 實現執行緒同步有哪幾種方法,而我只知道使用同步關鍵字synchronized來實現而已(⊙o⊙),,所以有必要來學習一下實現執行緒同步的幾種方法;各位看官,若有非議(不接受反駁),請不吝賜教! 實現執行緒同步的幾種方法 從我自己

執行執行的基本概念

一、前言:        提及多執行緒不得不提及“程序”這個概念。“百度百科”裡對“程序”的解析如下:程序是一個具有一定獨立功能的程式關於某個資料集合的一次執行活動。它是作業系統動態執行的基本單元,在傳統的作業系統中,程序既是基本的分配單元,也是基本的執行單元。      

執行建立執行的兩種方式以及其區別

第一種方式: 通過實現Runnable介面建立執行緒package cn.qblank.thread; /** * 多執行緒案例1 * @author Administrator * */ public class ThreadTest1 implements Run

執行synchronized同步程式碼塊

一、前言        使用synchronized宣告的方法在 某些情況下是有弊端的,比如A執行緒呼叫同步的方法執行一個長時間的任務,那麼B執行緒就必須等待比較長的時間才能執行,這種情況可以使用synchronized程式碼塊去優化程式碼執行時間,也就是通常所說的減少鎖的