wait、notify和notifyAll的使用


首先,wait、notify和notifyAll  這三個 都是Object類里的方法,可以用來控制線程的狀態

解釋:

如果對象調用了wait方法就會使持有該對象的線程把該對象的控制權交出去,然后處於等待狀態。

如果對象調用了notify方法就會通知某個正在等待這個對象的控制權的線程可以繼續運行。

如果對象調用了notifyAll方法就會通知所有等待這個對象控制權的線程繼續運行。

下來有個生產者消費者的列子, 提供理解:

package com.luxins.join.join;

import java.util.Queue;

public class Consumer implements Runnable {
    //背包
    Queue<String> bages;
    //大小
    int size;
    public Consumer(Queue<String> bages, int size) {
        this.bages = bages;
        this.size = size;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (bages) {
                while (bages.isEmpty()) {
                    try {
                        System.out.println("背包滿了了");
                        bages.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //睡眠1s
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                String bage = bages.remove();
                System.out.println("當前消費者的數量為:" + bage);

                //喚醒生產者(因為是雙向的,又是同一個對象bages下)
                bages.notifyAll();


            }
        }

    }
}

 

package com.luxins.join.join;

import java.util.Queue;

public class producer implements Runnable {
    Queue<String> bages;
    int size;

    public producer(Queue<String> bages, int size) {
        this.bages = bages;
        this.size = size;
    }

    @Override
    public void run() {
        int i = 0;
        while (true) {
            i++;
            synchronized (bages) {
                while (bages.size() == size) {
                    System.out.println("背包已經裝滿了");
                    try {
                        bages.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("當前生產者數量為:"+i);
                bages.add("bages"+i);
                //喚醒消費者
                bages.notifyAll();

            }

        }


    }
}
package com.luxins.join.join;

import java.util.LinkedList;
import java.util.Queue;

public class TestMain {
    public static void main(String[] args) {

        Queue<String> a = new LinkedList<>();
        int size = 10;
        Consumer consumer = new Consumer(a, size);
        producer producer = new producer(a, size);
        Thread t1 = new Thread(consumer);
        Thread t2 = new Thread(producer);
        t2.start();
        t1.start();



    }
}

 

 


做多 10條,可能在沒滿 10之前 ,就已經 用了
情況是隨機的
還有就是不一定就是生產一條 一定會消費一條

有可能生產了兩條 只消費一條 然后繼續生產

cup  不同,在不同電腦上出現的 效果 就不一樣

這個例子就是

一個線程修改了一個對象的值,而另個線程感
知到了變化,然后進行響應的操作,是雙向的,並且同一個對象的基礎之上(bage)

1. 從剛剛的例子來看,其實wait/notify本質上其實是一種條件的競爭,至少來說,
wait和notify方法一定是互斥存在的,既然要實現互斥,那么synchronized就是一
個很好的解決方法
2. wait和notify是用於實現多個線程之間的通信,而通信必然會存在一個通信的
載體,比如我們小時候玩的游戲,用兩個紙杯,中間用一根線連接,然后可以實現
比較遠距離的對話。而這根線就是載體,那么在這里也是一樣,wait/notify是基
於synchronized來實現通信的。也就是兩者必須要在同一個頻道也就是同一個鎖的
范圍內
 
最后,
其實說了這么多,一句話解釋就是 之所以我們應該盡量使用notifyAll()的原因就是,notify()非常容易導致死鎖
當然notifyAll並不一定都是優點,畢竟一次性將Wait Set中的線程都喚醒是一筆不菲的開銷,如果你能handle你的線程調度,那么使用notify()也是有好處的。

我們在調用wait()方法的時候,心里想的肯定是因為當前方法不滿足我們指定的條件,因此執行這個方法的線程需要等待直到其他線程改變了這個條件並且做出了通知。

那么為什么要把wait()方法放在循環而不是if判斷里呢,其實答案顯而易見,因為wait()的線程永遠不能確定其他線程會在什么狀態下notify(),

所以必須在被喚醒、搶占到鎖並且從wait()方法退出的時候再次進行指定條件的判斷,以決定是滿足條件往下執行呢還是不滿足條件再次wait()呢。

就像在本例中,如果只有一個生產者線程,一個消費者線程,那其實是可以用if代替while的,因為線程調度的行為是開發者可以預測的,

生產者線程只有可能被消費者線程喚醒,反之亦然,因此被喚醒時條件始終滿足,程序不會出錯。

但是這種情況只是多線程情況下極為簡單的一種,更普遍的是多個線程生產,多個線程消費,

那么就極有可能出現喚醒生產者的是另一個生產者或者喚醒消費者的是另一個消費者,這樣的情況下用if就必然會現類似過度生產或者過度消費的情況了,

典型如IndexOutOfBoundsException的異常。所以所有的java書籍都會建議開發者永遠都要把wait()放到循環語句里面





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM