最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

問題:

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

2. 多線程之間如何進行通信?

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

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

5. 為什么 wait 和 notify 方法要在同步塊中調用?

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

1. 線程通信的定義

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

線程的通信可以被定義為:當多個線程共同操作共享的資源時,線程間通過某種方式互相告知自己的狀態(tài),以避免無效的資源爭奪。

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

2. 為什么需要wait-notify?

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

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

在這里插入圖片描述

(2) 于是老王單開了一間休息室(調用 wait 方法),讓小南到休息室(WaitSet)等著去了,這時鎖釋放開, 其它人可以由老王隨機安排進屋

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

在這里插入圖片描述

(4) 小南于是可以離開休息室,重新進入競爭鎖的隊列

在這里插入圖片描述

java語言中“等待-通知”方式的線程間通信使用對象的wait()、notify()兩類方法來實現。每個java對象都有wait()、notify()兩類實例方法,并且wait()、notify()方法和對象的監(jiān)視器是緊密相關的。

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

3. wait方法和notify方法

java對象中的wait()、notify()兩類方法就如同信號開關,用于等待方和通知方之間的交互。

1、對象的wait()方法

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

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

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

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

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

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

2、對象的notify()方法

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

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

notify()方法有兩個版本:

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

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

小結:

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

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

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

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

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

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

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

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

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

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

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

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

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

在這里插入圖片描述

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

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

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

(4) WAITING :線程會在Owner 線程調用notify 或 notifyAll時喚醒,但喚醒后并不意味者立刻獲得鎖,仍需進入 EntryList 重新競爭;

5. wait方法和notify方法示例

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

小南并不能直接進入WaitSet休息室,而是獲取鎖進入房間后才能進入休息室,沒有鎖的話小南沒法進入房間,更沒法進入休息室。他進入休息室后就會釋放鎖,讓其他線程競爭鎖進入房間。

在這里插入圖片描述

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

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

小M此時獲取到了鎖,進入了房間,并喚醒了在休息室中等待的小王,小M如果獲取到鎖進行房間時沒有辦法喚醒在休息室等待的小王的,因為此時小M在門外,小王根本聽不到。

在這里插入圖片描述

使用notify()喚醒等待區(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線程開始執(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線程開始執(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.notify();
        }
    }
}

執(zhí)行結果:

t1線程開始執(zhí)行...
t2線程開始執(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線程開始執(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線程開始執(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í)行結果:

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

6. 為什么 wait 和 notify 方法要在同步塊中調用?

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

在這里插入圖片描述

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

wait()方法的原理:

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

notify()方法的原理:

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

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!  

相關文章

  • Java設計模式初識之備忘錄模式詳解

    Java設計模式初識之備忘錄模式詳解

    備忘錄設計模式(Memento Design Pattern)也叫作快照(Snapshot)模式,主要用于實現防丟失、撤銷、恢復等功能。本文將通過示例為大家介紹一些備忘錄模式的定義與使用,需要的可以參考一下
    2022-11-11
  • windows 32位eclipse遠程hadoop開發(fā)環(huán)境搭建

    windows 32位eclipse遠程hadoop開發(fā)環(huán)境搭建

    這篇文章主要介紹了windows 32位eclipse遠程hadoop開發(fā)環(huán)境搭建的相關資料,需要的朋友可以參考下
    2016-07-07
  • SpringBoot中swagger的使用

    SpringBoot中swagger的使用

    這篇文章主要介紹了SpringBoot中swagger的使用,文中有非常詳細的代碼示例,對正在學習swagger的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • java實現停車場管理系統(tǒng)

    java實現停車場管理系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現停車場管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • Spring中@Validated和@Valid區(qū)別淺析

    Spring中@Validated和@Valid區(qū)別淺析

    @Valid是javax.validation里的,?@Validated是@Valid?的一次封裝,是Spring提供的校驗機制使用,下面這篇文章主要給大家介紹了關于Spring中@Validated和@Valid區(qū)別的相關資料,需要的朋友可以參考下
    2022-04-04
  • 基于mybatis注解動態(tài)sql中foreach工具的方法

    基于mybatis注解動態(tài)sql中foreach工具的方法

    這篇文章主要介紹了mybatis注解動態(tài)sql中foreach工具方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot實現Word轉PDF和TXT的實踐分享

    SpringBoot實現Word轉PDF和TXT的實踐分享

    研發(fā)工作中難免會遇到一些奇奇怪怪的需求,就比如最近,客戶提了個新需求:上傳一個WORD文檔,要求通過系統(tǒng)把該文檔轉換成PDF和TXT,所以本文給大家分享了SpringBoot實現Word轉PDF和TXT的實踐,感興趣的朋友可以參考下
    2024-08-08
  • Java線程之間的共享與協(xié)作詳解

    Java線程之間的共享與協(xié)作詳解

    這篇文章主要介紹了Java線程之間的共享與協(xié)作詳解,進程是操作系統(tǒng)進行資源分配的最小單位,線程是進程的一個實體,是CPU調度和分派的基本單位,它是比經常更小的、能夠獨立運行的基本單位
    2022-07-07
  • Java中forEach使用lambda表達式,數組和集合的區(qū)別說明

    Java中forEach使用lambda表達式,數組和集合的區(qū)別說明

    這篇文章主要介紹了Java中forEach使用lambda表達式,數組和集合的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring?Boot?整合持久層之MyBatis

    Spring?Boot?整合持久層之MyBatis

    在實際開發(fā)中不僅僅是要展示數據,還要構成數據模型添加數據,這篇文章主要介紹了SpringBoot集成Mybatis操作數據庫,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08

最新評論

衢州市| 沙河市| 石屏县| 多伦县| 榕江县| 怀柔区| 松阳县| 靖西县| 长治市| 高安市| 静安区| 莲花县| 东平县| 崇义县| 于都县| 崇明县| 乌鲁木齐县| 宜兰市| 喀喇| 永嘉县| 扶沟县| 儋州市| 万山特区| 库车县| 青神县| 墨脱县| 宜州市| 衢州市| 武山县| 香港| 蒙山县| 宝鸡市| 崇明县| 南川市| 平邑县| 凉山| 凤翔县| 海盐县| 保康县| 廊坊市| 越西县|