亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java線程通信及線程虛假喚醒知識總結

 更新時間:2021年06月28日 10:27:26   作者:少年做自己的英雄  
今天給大家?guī)淼氖顷P于Java線程的相關知識,文章圍繞著Java線程通信及線程虛假喚醒的知識展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下

線程通信

線程在內部運行時,線程調度具有一定的透明性,程序通常無法控制線程的輪換執(zhí)行。但Java本身提供了一些機制來保證線程協(xié)調運行。

假設目前系統(tǒng)中有兩個線程,分別代表存款和取錢。當錢存進去,立馬就取出來挪入指定賬戶。這涉及到線程間的協(xié)作,使用到Object類提供的wait()、notify()、notifyAll()三個方法,其不屬于Thread類,而屬于Object,而這三個方法必須由監(jiān)視器對象來調用:

  • synchronized修飾的方法,因為該類的默認實例(this)就是同步監(jiān)視器,因此可以在同步方法中直接調用
  • synchronized修飾的同步代碼塊,同步監(jiān)視器是synchronized括號里的對象,因此必須使用該對象來調用

三個方法解釋如下:

  • wait():當前線程等待,釋放當前對象鎖,讓出CPU,直到其他線程使用notify或者notifyAll喚醒該線程
  • notify():喚醒在此同步監(jiān)視器上等待的單個線程,若存在多個線程,則隨機喚醒一個。執(zhí)行了notify不會馬上釋放鎖,只有完全退出synchronized代碼塊或者中途遇到wait,呈wait狀態(tài)的線程才可以去爭取該對象鎖
  • notifyAll():喚醒在此同步監(jiān)視器上的所有線程,同上。

現(xiàn)在用兩個同步方法分別代表存錢取錢

  • 當余額為0時,進入存錢流程,執(zhí)行存錢操作后,喚醒取錢線程
  • 當余額為0時,進入取錢流程,發(fā)現(xiàn)num==0,進入阻塞狀態(tài),等待被喚醒
/**
     * 存一塊錢
     *
     * @throws InterruptedException
     */
    public synchronized void increase() throws InterruptedException {
        // 當余額為1,說明已經存過錢,等待取錢。存錢方法阻塞
        if (num == 1) {
            this.wait();
        }
        // 執(zhí)行存錢操作
        num++;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        // 喚醒其他線程
        this.notifyAll();
    }
 
    /**
     * 取一塊錢
     *
     * @throws InterruptedException
     */
    public synchronized void decrease() throws InterruptedException {
        // 當余額為0,說明已經取過錢,等待存錢。取錢方法阻塞
        if (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        this.notifyAll();
    }

調用方法:

private int num = 0;
 
    public static void main(String[] args) {
        Test test = new Test();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.increase();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "存錢").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.decrease();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "取錢").start();
    }

結果沒有什么問題 

線程虛假喚醒

上述線程通信看起來似乎沒有什么問題,但若此時將存錢和取錢的人數(shù)各增加1,再看運行結果

private int num = 0;
 
    public static void main(String[] args) {
        Test test = new Test();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.increase();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "存錢1").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.decrease();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "取錢1").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.increase();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "存錢2").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.decrease();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "取錢2").start();
    }

產生的結果已經不是最初的只有0和1

造成這個結果的原因就是線程間的虛假喚醒

由于目前分別有多個取款和存款線程。假設其中一個存款線程執(zhí)行完畢,并使用wait釋放同步監(jiān)視器鎖定,那其余多個取款線程將同時被喚醒,此時余額為1,如果有10個同時取錢,那余額會變?yōu)?9,造成結果錯誤。

因此,每次線程從wait中被喚醒,都必須再次測試是否符合喚醒條件,如果不符合那就繼續(xù)等待。

由于多個線程被同時喚醒,在if(xxxx){wait();}處 if判斷只會執(zhí)行一次,當下一個被喚醒的線程過來時,由于if已經判斷過,則直接從wait后面的語句繼續(xù)執(zhí)行,因此將if換成while可解決該問題,下次被喚醒的線程過來,while重新判斷一下,發(fā)現(xiàn)上一個被喚醒的線程已經拿到鎖,因此這個被虛假喚醒的線程將繼續(xù)等待鎖。

 /**
     * 存一塊錢
     *
     * @throws InterruptedException
     */
    public synchronized void increase() throws InterruptedException {
        while (num == 1) {// 防止每次進來的喚醒線程只判斷一次造成虛假喚醒,替換成while
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        this.notifyAll();
    }
 
    /**
     * 取一塊錢
     *
     * @throws InterruptedException
     */
    public synchronized void decrease() throws InterruptedException {
        while (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        this.notifyAll();
    }

再次運行,結果正常:

到此這篇關于Java線程通信及線程虛假喚醒知識總結的文章就介紹到這了,更多相關Java線程通信及線程虛假喚醒內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java創(chuàng)建線程池的7種實現(xiàn)方法

    java創(chuàng)建線程池的7種實現(xiàn)方法

    在Java中線程池是一種管理線程的機制,它可以創(chuàng)建一組線程并重復使用它們,避免了創(chuàng)建和銷毀線程的開銷,這篇文章主要給大家介紹了關于java創(chuàng)建線程池的7種實現(xiàn)方法,需要的朋友可以參考下
    2023-10-10
  • springboot整合websocket最基礎入門使用教程詳解

    springboot整合websocket最基礎入門使用教程詳解

    這篇文章主要介紹了springboot整合websocket最基礎入門使用教程詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 解決SpringBoot運行Test時報錯:SpringBoot Unable to find

    解決SpringBoot運行Test時報錯:SpringBoot Unable to find

    這篇文章主要介紹了SpringBoot運行Test時報錯:SpringBoot Unable to find a @SpringBootConfiguration,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 深入IDEA Debug問題透析詳解

    深入IDEA Debug問題透析詳解

    這篇文章主要為大家介紹了深入IDEA Debug問題透析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Springboot整合mybatis開啟二級緩存的實現(xiàn)示例

    Springboot整合mybatis開啟二級緩存的實現(xiàn)示例

    在一級緩存中,是查詢兩次數(shù)據(jù)庫的,顯然這是一種浪費,既然SQL查詢相同,就沒有必要再次查庫了,直接利用緩存數(shù)據(jù)即可,這種思想就是MyBatis二級緩存的初衷,本文就詳細的介紹了Springboot整合mybatis開啟二級緩存,感興趣的可以了解一下
    2022-05-05
  • java簡單實現(xiàn)計算器

    java簡單實現(xiàn)計算器

    這篇文章主要為大家詳細介紹了java簡單實現(xiàn)計算器,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • 關于Idea清除緩存并重啟解決的問題

    關于Idea清除緩存并重啟解決的問題

    這篇文章主要介紹了關于Idea清除緩存并重啟解決的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • ArrayList和HashMap如何自己實現(xiàn)實例詳解

    ArrayList和HashMap如何自己實現(xiàn)實例詳解

    這篇文章主要介紹了 ArrayList和HashMap如何自己實現(xiàn)的相關資料,需要的朋友可以參考下
    2016-12-12
  • SpringBoot 關于Feign的超時時間配置操作

    SpringBoot 關于Feign的超時時間配置操作

    這篇文章主要介紹了SpringBoot 關于Feign的超時時間配置操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Redis結合AOP與自定義注解實現(xiàn)分布式緩存流程詳解

    Redis結合AOP與自定義注解實現(xiàn)分布式緩存流程詳解

    項目中如果查詢數(shù)據(jù)是直接到MySQL數(shù)據(jù)庫中查詢的話,會查磁盤走IO,效率會比較低,所以現(xiàn)在一般項目中都會使用緩存,目的就是提高查詢數(shù)據(jù)的速度,將數(shù)據(jù)存入緩存中,也就是內存中,這樣查詢效率大大提高
    2022-11-11

最新評論