天天看點

Java多線程學習之wait、notify/notifyAll 詳解

​點選“終碼一生”,關注,置頂公衆号,每日技術幹貨,第一時間送達!​

1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。

2、wait()使目前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊裡使用 wait()、notify/notifyAll() 方法。

3、 由于 wait()、notify/notifyAll() 在synchronized 代碼塊執行,說明目前線程一定是擷取了鎖的。

當線程執行wait()方法時候,會釋放目前的鎖,然後讓出CPU,進入等待狀态。

隻有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處于等待狀态的線程,然後繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。

也就是說,notify/notifyAll() 的執行隻是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執行情況。是以在程式設計中,盡量在使用了notify/notifyAll() 後立即退出臨界區,以喚醒其他線程讓其獲得鎖。

4、wait() 需要被try catch包圍,以便發生異常中斷也可以使wait等待的線程喚醒。

5、notify 和wait 的順序不能錯,如果A線程先執行notify方法,B線程在執行wait方法,那麼B線程是無法被喚醒的。

6、notify 和 notifyAll的差別

notify方法隻喚醒一個等待(對象的)線程并使該線程開始執行。是以如果有多個線程等待一個對象,這個方法隻會喚醒其中一個線程,選擇哪個線程取決于作業系統對多線程管理的實作。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程将會第一個處理取決于作業系統的實作。如果目前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。比如在生産者-消費者裡面的使用,每次都需要喚醒所有的消費者或是生産者,以判斷程式是否可以繼續往下執行。

7、在多線程中要測試某個條件的變化,使用if 還是while?

要注意,notify喚醒沉睡的線程後,線程會接着上次的執行繼續往下執行。是以在進行條件判斷時候,可以先把 wait 語句忽略不計來進行考慮;顯然,要確定程式一定要執行,并且要保證程式直到滿足一定的條件再執行,要使用while進行等待,直到滿足條件才繼續往下執行。如下代碼:

public class K {
    //狀态鎖
    private Object lock;
    //條件變量
    private int now,need;
    public void produce(int num){
        //同步
        synchronized (lock){
           //目前有的不滿足需要,進行等待,直到滿足條件
            while(now < need){
                try {
                    //等待阻塞
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
              System.out.println("我被喚醒了!");
            }
           // 做其他的事情
        }
    }
}      

顯然,隻有目前值滿足需要值的時候,線程才可以往下執行,是以,必須使用while 循環阻塞。注意,wait() 當被喚醒時候,隻是讓while循環繼續往下走.如果此處用if的話,意味着if繼續往下走,會跳出if語句塊。

8、實作生産者和消費者問題 

什麼是生産者-消費者問題呢?

Java多線程學習之wait、notify/notifyAll 詳解

如上圖,假設有一個公共的容量有限的池子,有兩種人,一種是生産者,另一種是消費者。需要滿足如下條件:

1、生産者産生資源往池子裡添加,前提是池子沒有滿,如果池子滿了,則生産者暫停生産,直到自己的生成能放下池子。

2、消費者消耗池子裡的資源,前提是池子的資源不為空,否則消費者暫停消耗,進入等待直到池子裡有資源數滿足自己的需求。

- 倉庫類

/**
 * 生産者和消費者的問題
 * wait、notify/notifyAll() 實作
 */
public class Storage1 implements AbstractStorage {
    //倉庫最大容量
    private final int MAX_SIZE = 100;
    //倉庫存儲的載體
    private LinkedList list = new LinkedList();

    //生産産品
    public void produce(int num){
        //同步
        synchronized (list){
            //倉庫剩餘的容量不足以存放即将要生産的數量,暫停生産
            while(list.size()+num > MAX_SIZE){
                System.out.println("【要生産的産品數量】:" + num + "\t【庫存量】:"
                        + list.size() + "\t暫時不能執行生産任務!");

                try {
                    //條件不滿足,生産阻塞
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            for(int i=0;i<num;i++){
                list.add(new Object());
            }

            System.out.println("【已經生産産品數】:" + num + "\t【現倉儲量為】:" + list.size());

            list.notifyAll();
        }
    }

    //消費産品
    public void consume(int num){
        synchronized (list){

            //不滿足消費條件
            while(num > list.size()){
                System.out.println("【要消費的産品數量】:" + num + "\t【庫存量】:"
                        + list.size() + "\t暫時不能執行生産任務!");

                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //消費條件滿足,開始消費
            for(int i=0;i<num;i++){
                list.remove();
            }

            System.out.println("【已經消費産品數】:" + num + "\t【現倉儲量為】:" + list.size());

            list.notifyAll();
        }
    }
}      

- 抽象倉庫類

public interface AbstractStorage {
    void consume(int num);
    void produce(int num);
}      

- 生産者

public class Producer extends Thread{
    //每次生産的數量
    private int num ;

    //所屬的倉庫
    public AbstractStorage abstractStorage;

    public Producer(AbstractStorage abstractStorage){
        this.abstractStorage = abstractStorage;
    }

    public void setNum(int num){
        this.num = num;
    }

    // 線程run函數
    @Override
    public void run()
    {
        produce(num);
    }

    // 調用倉庫Storage的生産函數
    public void produce(int num)
    {
        abstractStorage.produce(num);
    }
}      

- 消費者

public class Consumer extends Thread{
    // 每次消費的産品數量
    private int num;

    // 所在放置的倉庫
    private AbstractStorage abstractStorage1;

    // 構造函數,設定倉庫
    public Consumer(AbstractStorage abstractStorage1)
    {
        this.abstractStorage1 = abstractStorage1;
    }

    // 線程run函數
    public void run()
    {
        consume(num);
    }

    // 調用倉庫Storage的生産函數
    public void consume(int num)
    {
        abstractStorage1.consume(num);
    }

    public void setNum(int num){
        this.num = num;
    }
}      

- 測試

public class Test{
    public static void main(String[] args) {
        // 倉庫對象
        AbstractStorage abstractStorage = new Storage1();

        // 生産者對象
        Producer p1 = new Producer(abstractStorage);
        Producer p2 = new Producer(abstractStorage);
        Producer p3 = new Producer(abstractStorage);
        Producer p4 = new Producer(abstractStorage);
        Producer p5 = new Producer(abstractStorage);
        Producer p6 = new Producer(abstractStorage);
        Producer p7 = new Producer(abstractStorage);

        // 消費者對象
        Consumer c1 = new Consumer(abstractStorage);
        Consumer c2 = new Consumer(abstractStorage);
        Consumer c3 = new Consumer(abstractStorage);

        // 設定生産者産品生産數量
        p1.setNum(10);
        p2.setNum(10);
        p3.setNum(10);
        p4.setNum(10);
        p5.setNum(10);
        p6.setNum(10);
        p7.setNum(80);

        // 設定消費者産品消費數量
        c1.setNum(50);
        c2.setNum(20);
        c3.setNum(30);

        // 線程開始執行
        c1.start();
        c2.start();
        c3.start();

        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
        p6.start();
        p7.start();
    }
}      

- 輸出

【要消費的産品數量】:50    【庫存量】:0    暫時不能執行生産任務!
【要消費的産品數量】:20    【庫存量】:0    暫時不能執行生産任務!
【要消費的産品數量】:30    【庫存量】:0    暫時不能執行生産任務!
【已經生産産品數】:10    【現倉儲量為】:10
【要消費的産品數量】:30    【庫存量】:10    暫時不能執行生産任務!
【要消費的産品數量】:20    【庫存量】:10    暫時不能執行生産任務!
【要消費的産品數量】:50    【庫存量】:10    暫時不能執行生産任務!
【已經生産産品數】:10    【現倉儲量為】:20
【已經生産産品數】:10    【現倉儲量為】:30
【要消費的産品數量】:50    【庫存量】:30    暫時不能執行生産任務!
【已經消費産品數】:20    【現倉儲量為】:10
【要消費的産品數量】:30    【庫存量】:10    暫時不能執行生産任務!
【已經生産産品數】:10    【現倉儲量為】:20
【要消費的産品數量】:50    【庫存量】:20    暫時不能執行生産任務!
【要消費的産品數量】:30    【庫存量】:20    暫時不能執行生産任務!
【已經生産産品數】:10    【現倉儲量為】:30
【已經消費産品數】:30    【現倉儲量為】:0
【要消費的産品數量】:50    【庫存量】:0    暫時不能執行生産任務!
【已經生産産品數】:10    【現倉儲量為】:10
【要消費的産品數量】:50    【庫存量】:10    暫時不能執行生産任務!
【已經生産産品數】:80    【現倉儲量為】:90
【已經消費産品數】:50    【現倉儲量為】:40      

繼續閱讀