国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

Java多線程學(xué)習(xí)(四)等待/通知(wait/notify)機(jī)制

PiscesYE / 1637人閱讀

摘要:運(yùn)行可運(yùn)行狀態(tài)的線程獲得了時(shí)間片,執(zhí)行程序代碼。阻塞的情況分三種一等待阻塞運(yùn)行的線程執(zhí)行方法,會(huì)把該線程放入等待隊(duì)列中。死亡線程方法執(zhí)行結(jié)束,或者因異常退出了方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。

系列文章傳送門(mén):

Java多線程學(xué)習(xí)(一)Java多線程入門(mén)

Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1)

java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2)

Java多線程學(xué)習(xí)(三)volatile關(guān)鍵字

Java多線程學(xué)習(xí)(四)等待/通知(wait/notify)機(jī)制

Java多線程學(xué)習(xí)(五)線程間通信知識(shí)點(diǎn)補(bǔ)充

Java多線程學(xué)習(xí)(六)Lock鎖的使用

Java多線程學(xué)習(xí)(七)并發(fā)編程中一些問(wèn)題

系列文章將被優(yōu)先更新于微信公眾號(hào)“Java面試通關(guān)手冊(cè)”,歡迎廣大Java程序員和愛(ài)好技術(shù)的人員關(guān)注。

本節(jié)思維導(dǎo)圖:

思維導(dǎo)圖源文件+思維導(dǎo)圖軟件關(guān)注微信公眾號(hào):“Java面試通關(guān)手冊(cè)” 回復(fù)關(guān)鍵字:“Java多線程” 免費(fèi)領(lǐng)取。

一 等待/通知機(jī)制介紹 1.1 不使用等待/通知機(jī)制

當(dāng)兩個(gè)線程之間存在生產(chǎn)和消費(fèi)者關(guān)系,也就是說(shuō)第一個(gè)線程(生產(chǎn)者)做相應(yīng)的操作然后第二個(gè)線程(消費(fèi)者)感知到了變化又進(jìn)行相應(yīng)的操作。比如像下面的whie語(yǔ)句一樣,假設(shè)這個(gè)value值就是第一個(gè)線程操作的結(jié)果,doSomething()是第二個(gè)線程要做的事,當(dāng)滿足條件value=desire后才執(zhí)行doSomething()。

但是這里有個(gè)問(wèn)題就是:第二個(gè)語(yǔ)句不停過(guò)通過(guò)輪詢(xún)機(jī)制來(lái)檢測(cè)判斷條件是否成立。如果輪詢(xún)時(shí)間的間隔太小會(huì)浪費(fèi)CPU資源,輪詢(xún)時(shí)間的間隔太大,就可能取不到自己想要的數(shù)據(jù)。所以這里就需要我們今天講到的等待/通知(wait/notify)機(jī)制來(lái)解決這兩個(gè)矛盾

    while(value=desire){
        doSomething();
    }
1.2 什么是等待/通知機(jī)制?

通俗來(lái)講:

等待/通知機(jī)制在我們生活中比比皆是,一個(gè)形象的例子就是廚師和服務(wù)員之間就存在等待/通知機(jī)制。

廚師做完一道菜的時(shí)間是不確定的,所以菜到服務(wù)員手中的時(shí)間是不確定的;

服務(wù)員就需要去“等待(wait)”;

廚師把菜做完之后,按一下鈴,這里的按鈴就是“通知(nofity)”;

服務(wù)員聽(tīng)到鈴聲之后就知道菜做好了,他可以去端菜了。

用專(zhuān)業(yè)術(shù)語(yǔ)講:

等待/通知機(jī)制,是指一個(gè)線程A調(diào)用了對(duì)象O的wait()方法進(jìn)入等待狀態(tài),而另一個(gè)線程B調(diào)用了對(duì)象O的notify()/notifyAll()方法,線程A收到通知后退出等待隊(duì)列,進(jìn)入可運(yùn)行狀態(tài),進(jìn)而執(zhí)行后續(xù)操作。上訴兩個(gè)線程通過(guò)對(duì)象O來(lái)完成交互,而對(duì)象上的wait()方法notify()/notifyAll()方法的關(guān)系就如同開(kāi)關(guān)信號(hào)一樣,用來(lái)完成等待方和通知方之間的交互工作。

1.3 等待/通知機(jī)制的相關(guān)方法
方法名稱(chēng) 描述
notify() 隨機(jī)喚醒等待隊(duì)列中等待同一共享資源的 “一個(gè)線程”,并使該線程退出等待隊(duì)列,進(jìn)入可運(yùn)行狀態(tài),也就是notify()方法僅通知“一個(gè)線程”
notifyAll() 使所有正在等待隊(duì)列中等待同一共享資源的 “全部線程” 退出等待隊(duì)列,進(jìn)入可運(yùn)行狀態(tài)。此時(shí),優(yōu)先級(jí)最高的那個(gè)線程最先執(zhí)行,但也有可能是隨機(jī)執(zhí)行,這取決于JVM虛擬機(jī)的實(shí)現(xiàn)
wait() 使調(diào)用該方法的線程釋放共享資源鎖,然后從運(yùn)行狀態(tài)退出,進(jìn)入等待隊(duì)列,直到被再次喚醒
wait(long) 超時(shí)等待一段時(shí)間,這里的參數(shù)時(shí)間是毫秒,也就是等待長(zhǎng)達(dá)n毫秒,如果沒(méi)有通知就超時(shí)返回
wait(long,int) 對(duì)于超時(shí)時(shí)間更細(xì)力度的控制,可以達(dá)到納秒
二 等待/通知機(jī)制的實(shí)現(xiàn) 2.1 我的第一個(gè)等待/通知機(jī)制程序

MyList.java

public class MyList {
    private static List list = new ArrayList();

    public static void add() {
        list.add("anyString");
    }

    public static int size() {
        return list.size();
    }

}

ThreadA.java

public class ThreadA extends Thread {

    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                if (MyList.size() != 5) {
                    System.out.println("wait begin "
                            + System.currentTimeMillis());
                    lock.wait();
                    System.out.println("wait end  "
                            + System.currentTimeMillis());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

ThreadB.java

public class ThreadB extends Thread {
    private Object lock;

    public ThreadB(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    MyList.add();
                    if (MyList.size() == 5) {
                        lock.notify();
                        System.out.println("已發(fā)出通知!");
                    }
                    System.out.println("添加了" + (i + 1) + "個(gè)元素!");
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Run.java

public class Run {

    public static void main(String[] args) {

        try {
            Object lock = new Object();

            ThreadA a = new ThreadA(lock);
            a.start();

            Thread.sleep(50);

            ThreadB b = new ThreadB(lock);
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

運(yùn)行結(jié)果:

從運(yùn)行結(jié)果:"wait end 1521967322359"最后輸出可以看出,notify()執(zhí)行后并不會(huì)立即釋放鎖。下面我們會(huì)補(bǔ)充介紹這個(gè)知識(shí)點(diǎn)。

synchronized關(guān)鍵字可以將任何一個(gè)Object對(duì)象作為同步對(duì)象來(lái)看待,而Java為每個(gè)Object都實(shí)現(xiàn)了等待/通知(wait/notify)機(jī)制的相關(guān)方法,它們必須用在synchronized關(guān)鍵字同步的Object的臨界區(qū)內(nèi)。通過(guò)調(diào)用wait()方法可以使處于臨界區(qū)內(nèi)的線程進(jìn)入等待狀態(tài),同時(shí)釋放被同步對(duì)象的鎖。而notify()方法可以喚醒一個(gè)因調(diào)用wait操作而處于阻塞狀態(tài)中的線程,使其進(jìn)入就緒狀態(tài)。被重新喚醒的線程會(huì)視圖重新獲得臨界區(qū)的控制權(quán)也就是鎖,并繼續(xù)執(zhí)行wait方法之后的代碼。如果發(fā)出notify操作時(shí)沒(méi)有處于阻塞狀態(tài)中的線程,那么該命令會(huì)被忽略。

如果我們這里不通過(guò)等待/通知(wait/notify)機(jī)制實(shí)現(xiàn),而是使用如下的while循環(huán)實(shí)現(xiàn)的話,我們上面也講過(guò)會(huì)有很大的弊端。

 while(MyList.size() == 5){
        doSomething();
    }
2.2線程的基本狀態(tài)

上面幾章的學(xué)習(xí)中我們已經(jīng)掌握了與線程有關(guān)的大部分API,這些API可以改變線程對(duì)象的狀態(tài)。如下圖所示:

新建(new):新創(chuàng)建了一個(gè)線程對(duì)象。

可運(yùn)行(runnable):線程對(duì)象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,等待被線程調(diào)度選中,獲 取cpu的使用權(quán)。

運(yùn)行(running):可運(yùn)行狀態(tài)(runnable)的線程獲得了cpu時(shí)間片(timeslice),執(zhí)行程序代碼。

阻塞(block):阻塞狀態(tài)是指線程因?yàn)槟撤N原因放棄了cpu使用權(quán),也即讓出了cpu timeslice,暫時(shí)停止運(yùn)行。直到線程進(jìn)入可運(yùn)行(runnable)狀態(tài),才有 機(jī)會(huì)再次獲得cpu timeslice轉(zhuǎn)到運(yùn)行(running)狀態(tài)。阻塞的情況分三種:

(一). 等待阻塞:運(yùn)行(running)的線程執(zhí)行o.wait()方法,JVM會(huì)把該線程放 入等待隊(duì)列(waitting queue)中。

(二). 同步阻塞:運(yùn)行(running)的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖 被別的線程占用,則JVM會(huì)把該線程放入鎖池(lock pool)中。

(三). 其他阻塞: 運(yùn)行(running)的線程執(zhí)行Thread.sleep(long ms)或t.join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入可運(yùn)行(runnable)狀態(tài)。

死亡(dead):線程run()、main()方法執(zhí)行結(jié)束,或者因異常退出了run()方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。

備注:
可以用早起坐地鐵來(lái)比喻這個(gè)過(guò)程:

還沒(méi)起床:sleeping

起床收拾好了,隨時(shí)可以坐地鐵出發(fā):Runnable

等地鐵來(lái):Waiting

地鐵來(lái)了,但要排隊(duì)上地鐵:I/O阻塞

上了地鐵,發(fā)現(xiàn)暫時(shí)沒(méi)座位:synchronized阻塞

地鐵上找到座位:Running

到達(dá)目的地:Dead

2.3 notify()鎖不釋放

當(dāng)方法wait()被執(zhí)行后,鎖自動(dòng)被釋放,但執(zhí)行玩notify()方法后,鎖不會(huì)自動(dòng)釋放。必須執(zhí)行完otify()方法所在的synchronized代碼塊后才釋放。

下面我們通過(guò)代碼驗(yàn)證一下:

(完整代碼:https://github.com/Snailclimb/threadDemo/tree/master/src/wait_notifyHoldLock)

帶wait方法的synchronized代碼塊

            synchronized (lock) {
                System.out.println("begin wait() ThreadName="
                        + Thread.currentThread().getName());
                lock.wait();
                System.out.println("  end wait() ThreadName="
                        + Thread.currentThread().getName());
            }

帶notify方法的synchronized代碼塊

            synchronized (lock) {
                System.out.println("begin notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
            }

如果有三個(gè)同一個(gè)對(duì)象實(shí)例的線程a,b,c,a線程執(zhí)行帶wait方法的synchronized代碼塊然后bb線程執(zhí)行帶notify方法的synchronized代碼塊緊接著c執(zhí)行帶notify方法的synchronized代碼塊。

運(yùn)行效果如下:

這也驗(yàn)證了我們剛開(kāi)始的結(jié)論:必須執(zhí)行完notify()方法所在的synchronized代碼塊后才釋放。

2.4 當(dāng)interrupt方法遇到wait方法

當(dāng)線程呈wait狀態(tài)時(shí),對(duì)線程對(duì)象調(diào)用interrupt方法會(huì)出現(xiàn)InterrupedException異常。

Service.java

public class Service {
    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait()");
                lock.wait();
                System.out.println("  end wait()");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("出現(xiàn)異常了,因?yàn)槌蕎ait狀態(tài)的線程被interrupt了!");
        }
    }
}

ThreadA.java

public class ThreadA extends Thread {

    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }

}

Test.java

public class Test {

    public static void main(String[] args) {

        try {
            Object lock = new Object();

            ThreadA a = new ThreadA(lock);
            a.start();

            Thread.sleep(5000);

            a.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

運(yùn)行結(jié)果:

參考:

《Java多線程編程核心技術(shù)》

《Java并發(fā)編程的藝術(shù)》

如果你覺(jué)得博主的文章不錯(cuò),歡迎轉(zhuǎn)發(fā)點(diǎn)贊。你能從中學(xué)到知識(shí)就是我最大的幸運(yùn)。

歡迎關(guān)注我的微信公眾號(hào):“Java面試通關(guān)手冊(cè)”(分享各種Java學(xué)習(xí)資源,面試題,以及企業(yè)級(jí)Java實(shí)戰(zhàn)項(xiàng)目回復(fù)關(guān)鍵字免費(fèi)領(lǐng)取)。另外我創(chuàng)建了一個(gè)Java學(xué)習(xí)交流群(群號(hào):174594747),歡迎大家加入一起學(xué)習(xí),這里更有面試,學(xué)習(xí)視頻等資源的分享。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/69100.html

相關(guān)文章

  • [Java并發(fā)-5]用“等待-通知機(jī)制優(yōu)化循環(huán)等待

    摘要:在這個(gè)等待通知機(jī)制中,我們需要考慮以下四個(gè)要素。何時(shí)等待線程要求的條件不滿足就等待。是會(huì)隨機(jī)地通知等待隊(duì)列中的一個(gè)線程,而會(huì)通知等待隊(duì)列中的所有線程。 由上一篇文章你應(yīng)該已經(jīng)知道,在 破壞占用且等待條件 的時(shí)候,如果轉(zhuǎn)出賬本和轉(zhuǎn)入賬本不滿足同時(shí)在文件架上這個(gè)條件,就用死循環(huán)的方式來(lái)循環(huán)等待,核心代碼如下: // 一次性申請(qǐng)轉(zhuǎn)出賬戶和轉(zhuǎn)入賬戶,直到成功 while(!actr.apply...

    fxp 評(píng)論0 收藏0
  • Java線程學(xué)習(xí)(六)Lock鎖的使用

    摘要:返回與此鎖相關(guān)聯(lián)的給定條件等待的線程數(shù)的估計(jì)。查詢(xún)是否有線程正在等待獲取此鎖。為公平鎖,為非公平鎖線程運(yùn)行了獲得鎖定運(yùn)行結(jié)果公平鎖的運(yùn)行結(jié)果是有序的。 系列文章傳送門(mén): Java多線程學(xué)習(xí)(一)Java多線程入門(mén) Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2) Java多線程學(xué)習(xí)(三)volatile關(guān)鍵字 ...

    Caicloud 評(píng)論0 收藏0
  • Java線程匯總

    摘要:線程需要避免竟態(tài),死鎖以及很多其他共享狀態(tài)的并發(fā)性問(wèn)題。用戶線程在前臺(tái),守護(hù)線程在后臺(tái)運(yùn)行,為其他前臺(tái)線程提供服務(wù)。當(dāng)所有前臺(tái)線程都退出時(shí),守護(hù)線程就會(huì)退出。線程阻塞等待獲取某個(gè)對(duì)象鎖的訪問(wèn)權(quán)限。 1、多線程介紹 多線程優(yōu)點(diǎn) 資源利用率好 程序設(shè)計(jì)簡(jiǎn)單 服務(wù)器響應(yīng)更快 多線程缺點(diǎn) 設(shè)計(jì)更復(fù)雜 上下文切換的開(kāi)銷(xiāo) 增加資源消耗線程需要內(nèi)存維護(hù)本地的堆棧,同時(shí)需要操作系統(tǒng)資源管理線程。...

    Lsnsh 評(píng)論0 收藏0
  • JAVA線程間通信簡(jiǎn)介

    摘要:線程通信的目標(biāo)是使線程間能夠互相發(fā)送信號(hào)。但是,這個(gè)標(biāo)志已經(jīng)被第一個(gè)喚醒的線程清除了,所以其余醒來(lái)的線程將回到等待狀態(tài),直到下次信號(hào)到來(lái)。如果方法調(diào)用,而非,所有等待線程都會(huì)被喚醒并依次檢查信號(hào)值。 線程通信的目標(biāo)是使線程間能夠互相發(fā)送信號(hào)。另一方面,線程通信使線程能夠等待其他線程的信號(hào)。 showImg(http://segmentfault.com/img/bVbPLD); 例...

    CHENGKANG 評(píng)論0 收藏0
  • 并發(fā)學(xué)習(xí)筆記 (5)

    摘要:執(zhí)行會(huì)重新將設(shè)置為,并且通知喚醒其中一個(gè)若有的話在方法中調(diào)用了函數(shù)而處于等待狀態(tài)的線程。除此之外,我們需要記錄同一個(gè)線程重復(fù)對(duì)一個(gè)鎖對(duì)象加鎖的次數(shù)。競(jìng)爭(zhēng)失敗的線程處于就緒狀態(tài),長(zhǎng)期競(jìng)爭(zhēng)失敗的線程就會(huì)饑餓。 tutorials site Locks in java Locks (and other more advanced synchronization mechanisms...

    meteor199 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

PiscesYE

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<