天天看點

JDK1.9-等待喚醒機制

等待喚醒機制

1.1 線程間通信

**概念:**多個線程在處理同一個資源,但是處理的動作(線程的任務)卻不相同。

比如:線程A用來生成包子的,線程B用來吃包子的,包子可以了解為同一資源,線程A與線程B處理的動作,一個是生産,一個是消費,那麼線程A與線程B之間就存線上程通信問題。

JDK1.9-等待喚醒機制

為什麼要處理線程間通信:

多個線程并發執行時, 在預設情況下CPU是随機切換線程的,當我們需要多個線程來共同完成一件任務,并且我們希望他們有規律的執行, 那麼多線程之間需要一些協調通信,以此來幫我們達到多線程共同操作一份資料。

如何保證線程間通信有效利用資源:

多個線程在處理同一個資源,并且任務不同時,需要線程通信來幫助解決線程之間對同一個變量的使用或操作。 就是多個線程在操作同一份資料時, 避免對同一共享變量的争奪。也就是我們需要通過一定的手段使各個線程能有效的利用資源。而這種手段即—— 等待喚醒機制。

1.2 等待喚醒機制

什麼是等待喚醒機制

這是多個線程間的一種協作機制。談到線程我們經常想到的是線程間的競争(race),比如去争奪鎖,但這并不是故事的全部,線程間也會有協作機制。就好比在公司裡你和你的同僚們,你們可能存在在晉升時的競争,但更多時候你們更多是一起合作以完成某些任務。

就是在一個線程進行了規定操作後,就進入等待狀态(wait()), 等待其他線程執行完他們的指定代碼過後 再将其喚醒(notify());在有多個線程進行等待時, 如果需要,可以使用 notifyAll()來喚醒所有的等待線程。

wait/notify 就是線程間的一種協作機制。

等待喚醒中的方法

等待喚醒機制就是用于解決線程間通信的問題的,使用到的3個方法的含義如下:

  1. wait:線程不再活動,不再參與排程,進入 wait set 中,是以不會浪費 CPU 資源,也不會去競争鎖了,這時的線程狀态即是 WAITING。它還要等着别的線程執行一個特别的動作,也即是“通知(notify)”在這個對象上等待的線程從wait set 中釋放出來,重新進入到排程隊列(ready queue)中
  2. notify:則選取所通知對象的 wait set 中的一個線程釋放;例如,餐館有空位置後,等候就餐最久的顧客最先入座。
  3. notifyAll:則釋放所通知對象的 wait set 上的全部線程。

注意:

哪怕隻通知了一個等待的線程,被通知線程也不能立即恢複執行,因為它當國中斷的地方是在同步塊内,而此刻它已經不持有鎖,是以她需要再次嘗試去擷取鎖(很可能面臨其它線程的競争),成功後才能在當初調用 wait 方法之後的地方恢複執行。

總結如下:

  • 如果能擷取鎖,線程就從 WAITING 狀态變成 RUNNABLE 狀态;
  • 否則,從 wait set 出來,又進入 entry set,線程就從 WAITING 狀态又變成 BLOCKED 狀态

調用wait和notify方法需要注意的細節

  1. wait方法與notify方法必須要由同一個鎖對象調用。因為:對應的鎖對象可以通過notify喚醒使用同一個鎖對象調用的wait方法後的線程。
  2. wait方法與notify方法是屬于Object類的方法的。因為:鎖對象可以是任意對象,而任意對象的所屬類都是繼承了Object類的。
  3. wait方法與notify方法必須要在同步代碼塊或者是同步函數中使用。因為:必須要通過鎖對象調用這2個方法。

1.3 生産者與消費者問題

等待喚醒機制其實就是經典的“生産者與消費者”的問題。

就拿生産包子消費包子來說等待喚醒機制如何有效利用資源:

包子鋪線程生産包子,吃貨線程消費包子。當包子沒有時(包子狀态為false),吃貨線程等待,包子鋪線程生産包子(即包子狀态為true),并通知吃貨線程(解除吃貨的等待狀态),因為已經有包子了,那麼包子鋪線程進入等待狀态。接下來,吃貨線程能否進一步執行則取決于鎖的擷取情況。如果吃貨擷取到鎖,那麼就執行吃包子動作,包子吃完(包子狀态為false),并通知包子鋪線程(解除包子鋪的等待狀态),吃貨線程進入等待。包子鋪線程能否進一步執行則取決于鎖的擷取情況。
           

代碼示範:

包子資源類:

public class BaoZi {
     String  pier ;
     String  xianer ;
     boolean  flag = false ;//包子資源 是否存在  包子資源狀态
}
           

吃貨線程類:

public class ChiHuo extends Thread{
    private BaoZi bz;

    public ChiHuo(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }
    @Override
    public void run() {
        while(true){
            synchronized (bz){
                if(bz.flag == false){//沒包子
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("吃貨正在吃"+bz.pier+bz.xianer+"包子");
                bz.flag = false;
                bz.notify();
            }
        }
    }
}
           
public class BaoZiPu extends Thread {

    private BaoZi bz;

    public BaoZiPu(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }

    @Override
    public void run() {
        int count = 0;
        //造包子
        while(true){
            //同步
            synchronized (bz){
                if(bz.flag == true){//包子資源  存在
                    try {

                        bz.wait();

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 沒有包子  造包子
                System.out.println("包子鋪開始做包子");
                if(count%2 == 0){
                    // 冰皮  五仁
                    bz.pier = "冰皮";
                    bz.xianer = "五仁";
                }else{
                    // 薄皮  牛肉大蔥
                    bz.pier = "薄皮";
                    bz.xianer = "牛肉大蔥";
                }
                count++;

                bz.flag=true;
                System.out.println("包子造好了:"+bz.pier+bz.xianer);
                System.out.println("吃貨來吃吧");
                //喚醒等待線程 (吃貨)
                bz.notify();
            }
        }
    }
}
           
public class Demo {
    public static void main(String[] args) {
        //等待喚醒案例
        BaoZi bz = new BaoZi();

        ChiHuo ch = new ChiHuo("吃貨",bz);
        BaoZiPu bzp = new BaoZiPu("包子鋪",bz);

        ch.start();
        bzp.start();
    }
}
           
包子鋪開始做包子
包子造好了:冰皮五仁
吃貨來吃吧
吃貨正在吃冰皮五仁包子
包子鋪開始做包子
包子造好了:薄皮牛肉大蔥
吃貨來吃吧
吃貨正在吃薄皮牛肉大蔥包子
包子鋪開始做包子
包子造好了:冰皮五仁
吃貨來吃吧
吃貨正在吃冰皮五仁包子
           

繼續閱讀