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

為什么wait和notify必須放在synchronized中使用

 更新時(shí)間:2022年05月09日 14:29:02   作者:??Java中文社群????  
這篇文章主要介紹了為什么wait和notify必須放在synchronized中使用,文章圍繞主題的相關(guān)問(wèn)題展開(kāi)詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考以參考一下

前言:

在多線程編程中,wait 方法是讓當(dāng)前線程進(jìn)入休眠狀態(tài),直到另一個(gè)線程調(diào)用了 notify 或 notifyAll 方法之后,才能繼續(xù)恢復(fù)執(zhí)行。而在 Java 中,wait 和 notify/notifyAll 有著一套自己的使用格式要求,也就是在使用 wait 和 notify(notifyAll 的使用和 notify 類似,所以下文就只用 notify 用來(lái)指代二者)必須配合 synchronized 一起使用才行。

wait/notify基礎(chǔ)使用

wait 和 notify 的基礎(chǔ)方法如下:

Object lock = new Object();
new Thread(() -> {
    synchronized (lock) {
        try {
            System.out.println("wait 之前");
            // 調(diào)用 wait 方法
            lock.wait();
            System.out.println("wait 之后");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

Thread.sleep(100);
synchronized (lock) {
    System.out.println("執(zhí)行 notify");
    // 調(diào)用 notify 方法
    lock.notify();
}

以上代碼的執(zhí)行結(jié)果如下圖所示: 

image.png

wait/notify和synchronized一起用?

那問(wèn)題來(lái)了,是不是 wait 和 notify 一定要配合 synchronized 一起使用呢?wait 和 notify 單獨(dú)使用行不行呢? 我們嘗試將以上代碼中的 synchronized 代碼行刪除,實(shí)現(xiàn)代碼如下: 

image.png

 初看代碼好像沒(méi)啥問(wèn)題,編譯器也沒(méi)報(bào)錯(cuò),好像能“正常使用”,然而當(dāng)我們運(yùn)行以上程序時(shí)就會(huì)發(fā)生如下錯(cuò)誤: 

image.png

從上述結(jié)果可以看出:無(wú)論是 wait 還是 notify,如果不配合 synchronized 一起使用,在程序運(yùn)行時(shí)就會(huì)報(bào) IllegalMonitorStateException 非法的監(jiān)視器狀態(tài)異常,而且 notify 也不能實(shí)現(xiàn)程序的喚醒功能了。

原因分析

從上述的報(bào)錯(cuò)信息我們可以看出,JVM 在運(yùn)行時(shí)會(huì)強(qiáng)制檢查 wait 和 notify 有沒(méi)有在 synchronized 代碼中,如果沒(méi)有的話就會(huì)報(bào)非法監(jiān)視器狀態(tài)異常(IllegalMonitorStateException),但這也僅僅是運(yùn)行時(shí)的程序表象,那為什么 Java 要這樣設(shè)計(jì)呢? 其實(shí)這樣設(shè)計(jì)的原因就是為了防止多線程并發(fā)運(yùn)行時(shí),程序的執(zhí)行混亂問(wèn)題。初看這句話,好像是用來(lái)描述“鎖”的。然而實(shí)際情況也是如此,wait 和 notify 引入鎖就是來(lái)規(guī)避并發(fā)執(zhí)行時(shí)程序的執(zhí)行混亂問(wèn)題的。那這個(gè)“執(zhí)行混亂問(wèn)題”到底是啥呢?接下來(lái)我們繼續(xù)往下看。

wait和notify問(wèn)題復(fù)現(xiàn)

我們假設(shè) wait 和 notify 可以不加鎖,我們用它們來(lái)實(shí)現(xiàn)一個(gè)自定義阻塞隊(duì)列。 這里的阻塞隊(duì)列是指讀操作阻塞,也就是當(dāng)讀取數(shù)據(jù)時(shí),如果有數(shù)據(jù)就返回?cái)?shù)據(jù),如果沒(méi)有數(shù)據(jù)則阻塞等待數(shù)據(jù),實(shí)現(xiàn)代碼如下:

class MyBlockingQueue {
    // 用來(lái)保存數(shù)據(jù)的集合
    Queue<String> queue = new LinkedList<>();

    /**
     * 添加方法
     */
    public void put(String data) {
        // 隊(duì)列加入數(shù)據(jù)
        queue.add(data); 
        // 喚醒線程繼續(xù)執(zhí)行(這里的線程指的是執(zhí)行 take 方法的線程)
        notify(); // ③
    }

    /**
     * 獲取方法(阻塞式執(zhí)行)
     * 如果隊(duì)列里面有數(shù)據(jù)則返回?cái)?shù)據(jù),如果沒(méi)有數(shù)據(jù)就阻塞等待數(shù)據(jù)
     * @return
     */
    public String take() throws InterruptedException {
        // 使用 while 判斷是否有數(shù)據(jù)(這里使用 while 而非 if 是為了防止虛假喚醒)
        while (queue.isEmpty()) { // ①  
            // 沒(méi)有任務(wù),先阻塞等待
            wait(); // ②
        }
        return queue.remove(); // 返回?cái)?shù)據(jù)
    }
}

注意上述代碼,我們?cè)诖a中標(biāo)識(shí)了三個(gè)關(guān)鍵執(zhí)行步驟:①:判斷隊(duì)列中是否有數(shù)據(jù);②:執(zhí)行 wait 休眠操作;③:給隊(duì)列中添加數(shù)據(jù)并喚醒阻塞線程。 如果不強(qiáng)制要求添加 synchronized那么就會(huì)出現(xiàn)如下問(wèn)題:

步驟線程1線程2
1執(zhí)行步驟 ① 判斷當(dāng)前隊(duì)列中沒(méi)有數(shù)據(jù) 
2 執(zhí)行步驟 ③ 將數(shù)據(jù)添加到隊(duì)列,并喚醒線程1繼續(xù)執(zhí)行
3執(zhí)行步驟 ② 線程 1 進(jìn)入休眠狀態(tài) 

從上述執(zhí)行流程看出問(wèn)題了嗎? 如果 wait 和 notify 不強(qiáng)制要求加鎖,那么在線程 1 執(zhí)行完判斷之后,尚未執(zhí)行休眠之前,此時(shí)另一個(gè)線程添加數(shù)據(jù)到隊(duì)列中。然而這時(shí)線程 1 已經(jīng)執(zhí)行過(guò)判斷了,所以就會(huì)直接進(jìn)入休眠狀態(tài),從而導(dǎo)致隊(duì)列中的那條數(shù)據(jù)永久性不能被讀取,這就是程序并發(fā)運(yùn)行時(shí)“執(zhí)行結(jié)果混亂”的問(wèn)題。 然而如果配合 synchronized 一起使用的話,代碼就會(huì)變成以下這樣:

class MyBlockingQueue {
    // 用來(lái)保存任務(wù)的集合
    Queue<String> queue = new LinkedList<>();

    /**
     * 添加方法
     */
    public void put(String data) {
        synchronized (MyBlockingQueue.class) {
            // 隊(duì)列加入數(shù)據(jù)
            queue.add(data);
            // 為了防止 take 方法阻塞休眠,這里需要調(diào)用喚醒方法 notify
            notify(); // ③
        }
    }

    /**
     * 獲取方法(阻塞式執(zhí)行)
     * 如果隊(duì)列里面有數(shù)據(jù)則返回?cái)?shù)據(jù),如果沒(méi)有數(shù)據(jù)就阻塞等待數(shù)據(jù)
     * @return
     */
    public String take() throws InterruptedException {
        synchronized (MyBlockingQueue.class) {
            // 使用 while 判斷是否有數(shù)據(jù)(這里使用 while 而非 if 是為了防止虛假喚醒)
            while (queue.isEmpty()) {  // ①
                // 沒(méi)有任務(wù),先阻塞等待
                wait(); // ②
            }
        }
        return queue.remove(); // 返回?cái)?shù)據(jù)
    }
}

這樣改造之后,關(guān)鍵步驟 ① 和關(guān)鍵步驟 ② 就可以一起執(zhí)行了,從而當(dāng)線程執(zhí)行了步驟 ③ 之后,線程 1 就可以讀取到隊(duì)列中的那條數(shù)據(jù)了,它們的執(zhí)行流程如下:

步驟線程1線程2
1執(zhí)行步驟 ① 判斷當(dāng)前隊(duì)列沒(méi)有數(shù)據(jù) 
2執(zhí)行步驟 ② 線程進(jìn)入休眠狀態(tài) 
3 執(zhí)行步驟 ③ 將數(shù)據(jù)添加到隊(duì)列,并執(zhí)行喚醒操作
4線程被喚醒,繼續(xù)執(zhí)行 
5判斷隊(duì)列中有數(shù)據(jù),返回?cái)?shù)據(jù) 

這樣咱們的程序就可以正常執(zhí)行了,這就是為什么 Java 設(shè)計(jì)一定要讓 wait 和 notify 配合上 synchronized 一起使用的原因了。

總結(jié)

本文介紹了 wait 和 notify 的基礎(chǔ)使用,以及為什么 wait 和 notify/notifyAll 一定要配合 synchronized 使用的原因。如果 wait 和 notify/notifyAll 不強(qiáng)制和 synchronized 一起使用,那么在多線程執(zhí)行時(shí),就會(huì)出現(xiàn) wait 執(zhí)行了一半,然后又執(zhí)行了添加數(shù)據(jù)和 notify 的操作,從而導(dǎo)致線程一直休眠的缺陷。?

到此這篇關(guān)于為什么wait和notify必須放在synchronized中使用的文章就介紹到這了,更多相關(guān)wait與notify的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Springboot之Spring家族的技術(shù)體系

    Java Springboot之Spring家族的技術(shù)體系

    今天帶大家來(lái)學(xué)習(xí)Spring家族的技術(shù)體系,文中有非常詳細(xì)的圖文介紹及代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • 淺談javaSE 面向?qū)ο?Object類toString)

    淺談javaSE 面向?qū)ο?Object類toString)

    下面小編就為大家?guī)?lái)一篇淺談javaSE 面向?qū)ο?Object類toString)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • 詳解java基于MyBatis使用示例

    詳解java基于MyBatis使用示例

    這篇文章主要介紹了詳解java基于MyBatis使用示例,對(duì)學(xué)習(xí)MyBatis有一定的幫助,有需要的可以了解一下。
    2016-11-11
  • 使用Spring Expression Language (SpEL)全面解析表達(dá)式

    使用Spring Expression Language (SpEL)全面解析表達(dá)式

    這篇文章主要介紹了使用Spring Expression Language (SpEL)全面解析表達(dá)式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • SpringBoot中加密模塊的使用

    SpringBoot中加密模塊的使用

    本文主要介紹了SpringBoot中加密模塊的使用,包括對(duì)稱加密、非對(duì)稱加密和哈希加密等,同時(shí)還會(huì)提供相應(yīng)的代碼示例,感興趣的朋友可以參考一下
    2023-05-05
  • SpringBoot整合log4j日志與HashMap的底層原理解析

    SpringBoot整合log4j日志與HashMap的底層原理解析

    這篇文章主要介紹了SpringBoot整合log4j日志與HashMap的底層原理,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • java實(shí)現(xiàn)簡(jiǎn)單石頭剪刀布游戲

    java實(shí)現(xiàn)簡(jiǎn)單石頭剪刀布游戲

    這篇文章主要介紹了java實(shí)現(xiàn)簡(jiǎn)單石頭剪刀布游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • Java程序執(zhí)行cmd命令全過(guò)程

    Java程序執(zhí)行cmd命令全過(guò)程

    這篇文章主要介紹了Java程序執(zhí)行cmd命令全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Java  Option用法詳解

    Java  Option用法詳解

    Optional類是Java8為了解決null值判斷問(wèn)題,借鑒google guava類庫(kù)的Optional類而引入的一個(gè)同名Optional類,使用Optional類可以避免顯式的null值判斷,避免null導(dǎo)致的NPE,下面以一些典型場(chǎng)景為例,列出Optional API常用接口的用法,并附上相應(yīng)代碼,感興趣的朋友一起看看吧
    2024-01-01
  • MyBatis_Generator插件的安裝以及簡(jiǎn)單使用方法(圖解)

    MyBatis_Generator插件的安裝以及簡(jiǎn)單使用方法(圖解)

    下面小編就為大家?guī)?lái)一篇MyBatis_Generator插件的安裝以及簡(jiǎn)單使用方法(圖解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05

最新評(píng)論

浮梁县| 通化市| 祁门县| 兴仁县| 通城县| 阜新市| 阿合奇县| 洛浦县| 清丰县| 鹿泉市| 武汉市| 屯门区| 灵璧县| 万年县| 和平县| 孝昌县| 龙川县| 富顺县| 勐海县| 游戏| 望江县| 大冶市| 焦作市| 盐亭县| 黄梅县| 东乡县| 商丘市| 文成县| 尚志市| 民勤县| 萝北县| 芜湖县| 尼勒克县| 甘南县| 昭苏县| 祥云县| 枣庄市| 明星| 舞阳县| 株洲市| 淮安市|