91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中線程通信及線程虛假喚醒的示例分析

發布時間:2021-06-28 11:47:05 來源:億速云 閱讀:122 作者:小新 欄目:開發技術

這篇文章將為大家詳細講解有關Java中線程通信及線程虛假喚醒的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

線程通信

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

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

  • synchronized修飾的方法,因為該類的默認實例(this)就是同步監視器,因此可以在同步方法中直接調用

  • synchronized修飾的同步代碼塊,同步監視器是synchronized括號里的對象,因此必須使用該對象來調用

三個方法解釋如下:

  • wait():當前線程等待,釋放當前對象鎖,讓出CPU,直到其他線程使用notify或者notifyAll喚醒該線程

  • notify():喚醒在此同步監視器上等待的單個線程,若存在多個線程,則隨機喚醒一個。執行了notify不會馬上釋放鎖,只有完全退出synchronized代碼塊或者中途遇到wait,呈wait狀態的線程才可以去爭取該對象鎖

  • notifyAll():喚醒在此同步監視器上的所有線程,同上。

現在用兩個同步方法分別代表存錢取錢

  • 當余額為0時,進入存錢流程,執行存錢操作后,喚醒取錢線程

  • 當余額為0時,進入取錢流程,發現num==0,進入阻塞狀態,等待被喚醒

/**
     * 存一塊錢
     *
     * @throws InterruptedException
     */
    public synchronized void increase() throws InterruptedException {
        // 當余額為1,說明已經存過錢,等待取錢。存錢方法阻塞
        if (num == 1) {
            this.wait();
        }
        // 執行存錢操作
        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();
    }

結果沒有什么問題 

Java中線程通信及線程虛假喚醒的示例分析

線程虛假喚醒

上述線程通信看起來似乎沒有什么問題,但若此時將存錢和取錢的人數各增加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

Java中線程通信及線程虛假喚醒的示例分析

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

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

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

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

 /**
     * 存一塊錢
     *
     * @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中線程通信及線程虛假喚醒的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

西乡县| 樟树市| 沾化县| 鹤庆县| 吴桥县| 黑水县| 饶平县| 依兰县| 吴旗县| 黄冈市| 卓尼县| 盘锦市| 京山县| 青河县| 西城区| 杨浦区| 青阳县| 惠来县| 盐津县| 合阳县| 玉树县| 阿坝| 图片| 彰化市| 南木林县| 报价| 彭阳县| 铜川市| 沅陵县| 开化县| 新巴尔虎右旗| 垦利县| 浦东新区| 德钦县| 盐城市| 楚雄市| 湖州市| 随州市| 鄯善县| 保定市| 固镇县|