1. 程式人生 > 其它 >多執行緒------等待喚醒機制、執行緒間通訊、生產者與消費者問題

多執行緒------等待喚醒機制、執行緒間通訊、生產者與消費者問題

技術標籤:javase多執行緒

執行緒間通訊

概述:

多個執行緒在處理同一個資源,但是處理的動作(執行緒的任務)卻不相同。

比如:執行緒A用來生成包子的,執行緒B用來吃包子的,包子可以理解為同一資源,執行緒A與執行緒B處理的動作,一個是生產,一個是消費,那麼執行緒A與執行緒B之間就存線上程通訊問題。
在這裡插入圖片描述

為什麼要處理執行緒間通訊:

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

如何保證執行緒間通訊有效利用資源:

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

等待喚醒機制

什麼是等待喚醒機制

  • 這是多個執行緒間的一種協作機制。談到執行緒我們經常想到的是執行緒間的競爭(race),比如去爭奪鎖,但這並不是故事的全部,執行緒間也會有協作機制。
  • 在一個執行緒進行了規定操作後,就進入等待狀態(wait()), 等待其他執行緒執行完他們的指定程式碼過後,再將其喚醒(notify());在有多個執行緒進行等待時, 如果需要,可以使用 notifyAll()來喚醒所有的等待執行緒。
  • wait/notify 就是執行緒間的一種協作機制。
  • 等待喚醒機制就是用於解決執行緒間通訊的問題的。

等待喚醒的3個關鍵方法

方法:

  • wait:執行緒不再活動,不再參與排程,進入 wait set 中,因此不會浪費 CPU 資源,也不會去競爭鎖了,這時的執行緒狀態即是 WAITING。它還要等著別的執行緒執行一個特別的動作
    ,也即是“通知(notify)”在這個物件上等待的執行緒從wait set 中釋放出來,重新進入到排程佇列(ready queue)中
  • notify:選取所通知物件的 wait set 中的一個執行緒釋放—隨機喚醒在該鎖物件上等待的一個執行緒!!!
  • notifyAll:則釋放所通知物件的 wait set 上的全部執行緒------喚醒在該鎖物件上等待的所有執行緒!!!

注意:

  • 上述3個方法的執行都是通過鎖物件.方法() 執行!!!
  • 哪怕只通知了一個等待的執行緒,被通知執行緒也不能立即恢復執行,因為它當初中斷的地方是在同步塊內,而此刻它已經不持有鎖,所以需要再次嘗試去獲取鎖(很可能面臨其它執行緒的競爭)成功後才能在當初呼叫 wait 方法之後的地方恢復執行。
  • 執行通知方法的執行緒也並不是直接釋放鎖,而是等到執行完 同步程式碼塊/同步方法 後,才釋放鎖,然後由等待該同步鎖的執行緒(包括剛剛被通知方法喚醒的執行緒,以及阻塞佇列中的執行緒)去競爭
  • 如果能獲取鎖,執行緒就從 WAITING 狀態變成 RUNNABLE 狀態;否則,從 wait set 出來,又進入 entry set,執行緒就從 WAITING 狀態又變成 BLOCKED 狀態

生產者與消費者問題

------等待喚醒機制的應用------實現執行緒間通訊

程式碼演示:

  1. 包子資源類:
public class BaoZi {
     String  pier ;
     String  xianer ;
     boolean  flag = false ;//包子資源 是否存在  包子資源狀態
}
  1. 吃貨執行緒類:
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();
            }
        }
    }
}
  1. 包子鋪執行緒類:
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();
            }
        }
    }
}
  1. 測試類:
public class Test {
    public static void main(String[] args) {
        //等待喚醒案例
        BaoZi bz = new BaoZi();

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

        ch.start();
        bzp.start();
    }
}