欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java線程通信之wait-notify通信方式詳解

 更新時(shí)間:2022年03月01日 16:57:59   作者:小小茶花女  
這篇文章主要為大家詳細(xì)介紹了Java線程通信之wait-notify通信方式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

問(wèn)題:

1.線程 wait()方法使用有什么前提?

2. 多線程之間如何進(jìn)行通信?

3. Java 中 notify 和 notifyAll 有什么區(qū)別?

4. 為什么 wait/notify/notifyAll 這些方法不在 thread 類里面?

5. 為什么 wait 和 notify 方法要在同步塊中調(diào)用?

6. notify()和 notifyAll()有什么區(qū)別?

1. 線程通信的定義

線程是操作系統(tǒng)調(diào)度的最小單位,有自己的??臻g,可以按照既定的代碼逐步執(zhí)行,但是如果每個(gè)線程間都孤立地運(yùn)行,就會(huì)造資源浪費(fèi)。所以在現(xiàn)實(shí)中,如果需要多個(gè)線程按照指定的規(guī)則共同完成一個(gè)任務(wù),那么這些線程之間就需要互相協(xié)調(diào),這個(gè)過(guò)程被稱為線程的通信。

線程的通信可以被定義為:當(dāng)多個(gè)線程共同操作共享的資源時(shí),線程間通過(guò)某種方式互相告知自己的狀態(tài),以避免無(wú)效的資源爭(zhēng)奪。

線程間通信的方式可以有很多種:等待-通知、共享內(nèi)存、管道流。“等待-通知”通信方式是Java中使用普遍的線程間通信方式,其經(jīng)典的案例是“生產(chǎn)者-消費(fèi)者”模式。

2. 為什么需要wait-notify?

場(chǎng)景:有幾個(gè)小孩都想進(jìn)入房間內(nèi)使用算盤(CPU)進(jìn)行計(jì)算,老王(操作系統(tǒng))就使用了一把鎖(synchronized)讓同一時(shí)間只有一個(gè)小孩能進(jìn)入房間使用算盤,于是他們排隊(duì)進(jìn)入房間。

(1) 小南最先獲取到了鎖,進(jìn)入到房間內(nèi),但是由于條件不滿足(沒(méi)煙干不了活),小南不能繼續(xù)進(jìn)行計(jì)算 ,但小南如果一直占用著鎖,其它人就得一直阻塞,效率太低。

在這里插入圖片描述

(2) 于是老王單開(kāi)了一間休息室(調(diào)用 wait 方法),讓小南到休息室(WaitSet)等著去了,這時(shí)鎖釋放開(kāi), 其它人可以由老王隨機(jī)安排進(jìn)屋

(3) 直到小M將煙送來(lái),大叫一聲 [ 你的煙到了 ] (調(diào)用 notify 方法)

在這里插入圖片描述

(4) 小南于是可以離開(kāi)休息室,重新進(jìn)入競(jìng)爭(zhēng)鎖的隊(duì)列

在這里插入圖片描述

java語(yǔ)言中“等待-通知”方式的線程間通信使用對(duì)象的wait()、notify()兩類方法來(lái)實(shí)現(xiàn)。每個(gè)java對(duì)象都有wait()、notify()兩類實(shí)例方法,并且wait()、notify()方法和對(duì)象的監(jiān)視器是緊密相關(guān)的。

wait()、notify()兩類方法在數(shù)量上不止兩個(gè)。wait()、notify()兩類方法不屬于Thread類,而是屬于java對(duì)象實(shí)例。

3. wait方法和notify方法

java對(duì)象中的wait()、notify()兩類方法就如同信號(hào)開(kāi)關(guān),用于等待方和通知方之間的交互。

1、對(duì)象的wait()方法

對(duì)象的wait()方法的主要作用是讓當(dāng)前線程阻塞并等待被喚醒。wait()方法與對(duì)象監(jiān)視器緊密相關(guān),使用wait()方法時(shí)一定要放在同步塊中。wait()方法的調(diào)用方法如下:

public class Main {
    static final Object lock = new Object();
    public static void method1() throws InterruptedException {
        synchronized( lock ) {
            lock.wait();
        }
    }
}

Object類中的wait()方法有三個(gè)版本:

(1) void wait():當(dāng)前線程調(diào)用了同步對(duì)象lockwait()實(shí)例方法后,將導(dǎo)致當(dāng)前的線程等待,當(dāng)前線程進(jìn)入lock的監(jiān)視器WaitSet,等待被其他線程喚醒;

(2) void wait(long timeout):限時(shí)等待。導(dǎo)致當(dāng)前的線程等待,等待被其他線程喚醒,或者指定的時(shí)間timeout用完,線程不再等待;

(3) void wait(long timeout,int nanos):高精度限時(shí)等待,其主要作用是更精確地控制等待時(shí)間。參數(shù)nanos是一個(gè)附加的納秒級(jí)別的等待時(shí)間;

2、對(duì)象的notify()方法

對(duì)象的notify()方法的主要作用是喚醒在等待的線程。notify()方法與對(duì)象監(jiān)視器緊密相關(guān),調(diào)用notify()方法時(shí)也需要放在同步塊中。notify()方法的調(diào)用方法如下:

public class Main {
    static final Object lock = new Object();
    public static void method1() throws InterruptedException {
        synchronized( lock ) {
            lock.notify();
        }
    }
}

notify()方法有兩個(gè)版本:

(1)void notify()lock.notify()調(diào)用后,喚醒lock監(jiān)視器等待集中的第一條等待線程;被喚醒的線程進(jìn)入EntryList,其狀態(tài)從WAITING變成BLOCKED。

(2) void notifyAll()lock.notifyAll()被調(diào)用后,喚醒lock監(jiān)視器等待集中的全部等待線程,所有被喚醒的線程進(jìn)入EntryList,線程狀態(tài)從WAITING變成BLOCKED。

小結(jié):

obj.wait():讓進(jìn)入Object監(jiān)視器的線程到waitset等待

obj.notify():在Object上正在waitset等待的線程中挑一個(gè)喚醒

obj.notifyAll():讓在Object上正在waitset等待的線程全部喚醒

4. wait方法和notify方法的原理

對(duì)象的wait()方法的核心原理大致如下:

(1) 當(dāng)線程調(diào)用了lock(某個(gè)同步鎖對(duì)象)的wait()方法后,jvm會(huì)將當(dāng)前線程加入lock監(jiān)視器的WaitSet(等待集),等待被其他線程喚醒。

(2) 當(dāng)前線程會(huì)釋放lock對(duì)象監(jiān)視器的Owner權(quán)利,讓其他線程可以搶奪lock對(duì)象的監(jiān)視器。

(3) 讓當(dāng)前線程等待,其狀態(tài)變成WAITING。在線程調(diào)用了同步對(duì)象lock的wait()方法之后,同步對(duì)象lock的監(jiān)視器內(nèi)部狀態(tài)大致如圖2-15所示。

對(duì)象的notify()或者notifyAll()??????????????方法的原理大致如下:

(1) 當(dāng)線程調(diào)用了lock(某個(gè)同步鎖對(duì)象)的notify()方法后,jvm會(huì)喚醒lock監(jiān)視器WaitSet中的第一條等待線程。

(2) 當(dāng)線程調(diào)用了locknotifyAll()方法后,jvm會(huì)喚醒lock監(jiān)視器WaitSet中的所有等待線程。

(3) 等待線程被喚醒后,會(huì)從監(jiān)視器的WaitSet移動(dòng)到EntryList,線程具備了排隊(duì)搶奪監(jiān)視器Owner權(quán)利的資格,其狀態(tài)從WAITING變成BLOCKED。

(4) EntryList中的線程搶奪到監(jiān)視器的Owner權(quán)利之后,線程的狀態(tài)從BLOCKED變成Runnable,具備重新執(zhí)行的資格。

在這里插入圖片描述

(1) Owner 線程發(fā)現(xiàn)條件不滿足,調(diào)用wait 方法,即可進(jìn)入WaitSet,變?yōu)?WAITING 狀態(tài) ;

(2) BLOCKED 和WAITING 的線程都處于阻塞狀態(tài),不占用CPU時(shí)間片 ;

(3) BLOCKED:線程會(huì)在Owner 線程釋放鎖時(shí)喚醒 ;

(4) WAITING :線程會(huì)在Owner 線程調(diào)用notify 或 notifyAll時(shí)喚醒,但喚醒后并不意味者立刻獲得鎖,仍需進(jìn)入 EntryList 重新競(jìng)爭(zhēng);

5. wait方法和notify方法示例

1、進(jìn)入Object監(jiān)視器的線程才能調(diào)用wait()方法

小南并不能直接進(jìn)入WaitSet休息室,而是獲取鎖進(jìn)入房間后才能進(jìn)入休息室,沒(méi)有鎖的話小南沒(méi)法進(jìn)入房間,更沒(méi)法進(jìn)入休息室。他進(jìn)入休息室后就會(huì)釋放鎖,讓其他線程競(jìng)爭(zhēng)鎖進(jìn)入房間。

在這里插入圖片描述

public class Main {
    static final Object lock = new Object();
    public static void main(String[] args) {
        synchronized (lock){
            try {
                // 只有成為了monitor對(duì)象的owner,即獲得了對(duì)象鎖之后,才有資格進(jìn)入waitset等待
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2、進(jìn)入Object監(jiān)視器的線程才能調(diào)用notify()方法

小M此時(shí)獲取到了鎖,進(jìn)入了房間,并喚醒了在休息室中等待的小王,小M如果獲取到鎖進(jìn)行房間時(shí)沒(méi)有辦法喚醒在休息室等待的小王的,因?yàn)榇藭r(shí)小M在門外,小王根本聽(tīng)不到。

在這里插入圖片描述

使用notify()喚醒等待區(qū)的一個(gè)線程:

public class Main {
    static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1線程開(kāi)始執(zhí)行...");
            synchronized (lock){
                try {
                    // 讓t1線程在lock鎖的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 被喚醒后,繼續(xù)執(zhí)行
                System.out.println("線程t1被喚醒...");
            }
        },"t1");
        t1.start();
        Thread t2 = new Thread(()->{
            System.out.println("t2線程開(kāi)始執(zhí)行...");
            synchronized (lock){
                try {
                    // 讓t2線程在lock鎖的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 被喚醒后,繼續(xù)執(zhí)行
            System.out.println("線程t2被喚醒...");
        },"t2");
        t2.start();
        Thread.sleep(2000);
        System.out.println("喚醒lock鎖上等待的線程...");
        synchronized (lock){
            // 主線程拿到鎖后,喚醒正在休息室中等待的某一個(gè)線程
            lock.notify();
        }
    }
}

執(zhí)行結(jié)果:

t1線程開(kāi)始執(zhí)行...
t2線程開(kāi)始執(zhí)行...
喚醒lock鎖上等待的線程...
線程t1被喚醒...

使用notifyAll()喚醒等待區(qū)所有的線程:

public class Main {
    static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1線程開(kāi)始執(zhí)行...");
            synchronized (lock){
                try {
                    // 讓t1線程在lock鎖的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 被喚醒后,繼續(xù)執(zhí)行
                System.out.println("線程t1被喚醒...");
            }
        },"t1");

        t1.start();
        Thread t2 = new Thread(()->{
            System.out.println("t2線程開(kāi)始執(zhí)行...");
            synchronized (lock){
                try {
                    // 讓t2線程在lock鎖的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 被喚醒后,繼續(xù)執(zhí)行
            System.out.println("線程t2被喚醒...");
        },"t2");
        t2.start();

        Thread.sleep(2000);

        System.out.println("喚醒lock鎖上等待的線程...");
        synchronized (lock){
            // 主線程拿到鎖后,喚醒正在休息室中等待的所有線程
            lock.notifyAll();
        }
    }
}

執(zhí)行結(jié)果:

t1線程開(kāi)始執(zhí)行...
t2線程開(kāi)始執(zhí)行...
喚醒lock鎖上等待的線程...
線程t2被喚醒...
線程t1被喚醒...

6. 為什么 wait 和 notify 方法要在同步塊中調(diào)用?

在調(diào)用同步對(duì)象的wait()和notify()系列方法時(shí),“當(dāng)前線程”必須擁有該對(duì)象的同步鎖,也就是說(shuō),wait()和notify()系列方法需要在同步塊中使用,否則JVM會(huì)拋出類似如下的異常:

在這里插入圖片描述

為什么wait和notify不在synchronized同步塊的內(nèi)部使用會(huì)拋出異常呢?這需要從wait()和notify()方法的原理說(shuō)起。

wait()方法的原理:

首先,JVM會(huì)釋放當(dāng)前線程的對(duì)象鎖監(jiān)視器的Owner資格;其次,JVM會(huì)將當(dāng)前線程移入監(jiān)視器的WaitSet隊(duì)列,而這些操作都和對(duì)象鎖監(jiān)視器是相關(guān)的。所以,wait()方法必須在synchronized同步塊的內(nèi)部調(diào)用。在當(dāng)前線程執(zhí)行wait()方法前,必須通過(guò)synchronized()方法成為對(duì)象鎖的監(jiān)視器的Owner。

notify()方法的原理:

JVM從對(duì)象鎖的監(jiān)視器的WaitSet隊(duì)列移動(dòng)一個(gè)線程到其EntryList隊(duì)列,這些操作都與對(duì)象鎖的監(jiān)視器有關(guān)。所以,notify()方法也必須在synchronized同步塊的內(nèi)部調(diào)用。在執(zhí)行notify()方法前,當(dāng)前線程也必須通過(guò)synchronized()方法成為對(duì)象鎖的監(jiān)視器的Owner。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!  

相關(guān)文章

  • Centos中yum方式安裝java的實(shí)現(xiàn)示例

    Centos中yum方式安裝java的實(shí)現(xiàn)示例

    這篇文章主要介紹了Centos中yum方式安裝java的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Java中ArrayList和LinkedList的區(qū)別

    Java中ArrayList和LinkedList的區(qū)別

    ArrayList和LinkedList在這個(gè)方法上存在一定的性能差異,本文就介紹了Java中ArrayList和LinkedList的區(qū)別,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • SiteMesh如何結(jié)合Freemarker及velocity使用

    SiteMesh如何結(jié)合Freemarker及velocity使用

    這篇文章主要介紹了SiteMesh如何結(jié)合Freemarker及velocity使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • java讀取excel圖片導(dǎo)入代碼示例(親測(cè)有效)

    java讀取excel圖片導(dǎo)入代碼示例(親測(cè)有效)

    在日常工作中,我們經(jīng)常要將一些照片插入到Excel表格中,這篇文章主要給大家介紹了關(guān)于java讀取excel圖片導(dǎo)入的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • Java NIO實(shí)戰(zhàn)之聊天室功能詳解

    Java NIO實(shí)戰(zhàn)之聊天室功能詳解

    這篇文章主要介紹了Java NIO實(shí)戰(zhàn)之聊天室功能,結(jié)合實(shí)例形式詳細(xì)分析了java NIO聊天室具體的服務(wù)端、客戶端相關(guān)實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下
    2019-11-11
  • Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

    Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

    這篇文章主要介紹了Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java 由淺入深帶你掌握?qǐng)D的遍歷

    Java 由淺入深帶你掌握?qǐng)D的遍歷

    圖的遍歷是指,從給定圖中任意指定的頂點(diǎn)(稱為初始點(diǎn))出發(fā),按照某種搜索方法沿著圖的邊訪問(wèn)圖中的所有頂點(diǎn),使每個(gè)頂點(diǎn)僅被訪問(wèn)一次,這個(gè)過(guò)程稱為圖的遍歷。遍歷過(guò)程中得到的頂點(diǎn)序列稱為圖遍歷序列
    2022-03-03
  • java實(shí)現(xiàn)求只出現(xiàn)一次的數(shù)字

    java實(shí)現(xiàn)求只出現(xiàn)一次的數(shù)字

    本文主要介紹了java實(shí)現(xiàn)求只出現(xiàn)一次的數(shù)字,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Java中泛型總結(jié)(推薦)

    Java中泛型總結(jié)(推薦)

    這篇文章主要介紹了Java中泛型總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • JAVA面向?qū)ο?封裝原理及實(shí)例解析

    JAVA面向?qū)ο?封裝原理及實(shí)例解析

    這篇文章主要介紹了JAVA面向?qū)ο?封裝原理及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02

最新評(píng)論