等待喚醒機制---Day25

線程間通訊多線程

  概念:多個線程在處理同一個資源,可是處理的動做(線程的任務)卻不相同。併發

  好比:線程A用來生成包子的,線程B用來吃包子的,包子能夠理解爲同一資源,線程A與線程B處理的動做,一個 是生產,一個是消費,那麼線程A與線程B之間就存在線程通訊問題。ide

  爲何要處理線程間通訊:函數

    多個線程併發執行時, 在默認狀況下CPU是隨機切換線程的,當咱們須要多個線程來共同完成一項任務時,咱們都知道執行任務時通常都是按規律或者規則來執行,因此多線程執行任務也是同樣,須要規律來協調通訊,以此來幫咱們達到多線程共同操做一項任務(一份數據)。測試

  如何保證線程間通訊有效執行任務(利用資源):this

    多個線程在處理同一個資源,而且任務不一樣時,須要線程通訊來幫助解決線程之間對同一個變量的使用或操做。 由於多個線程在操做同一份數據時, 要避免對同一共享變量的爭奪。所以咱們須要經過必定的手段使各個線程能有效的利用資源。而這種手段即—— 等待喚醒機制spa

等待喚醒機制線程

  什麼時等待喚醒機制code

    這是多個線程間的一種協做機制。談到線程咱們常常想到的是線程間的競爭(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個方法。

等待喚醒機制是「生產者與消費者之間的關係」

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

    包子鋪線程生產包子,吃貨線程消費包子。當包子沒有時(包子狀態爲false),吃貨線程等待,包子鋪線程生產包子 (即包子狀態爲true),並通知吃貨線程(解除吃貨的等待狀態),由於已經有包子了,那麼包子鋪線程進入等待狀態。 接下來,吃貨線程可否進一步執行則取決於鎖的獲取狀況。若是吃貨獲取到鎖,那麼就執行吃包子動做,包子吃完(包 子狀態爲false),並通知包子鋪線程(解除包子鋪的等待狀態),吃貨線程進入等待。包子鋪線程可否進一步執行則取決於鎖的獲取狀況

包子類

 1 package demosummary.waitingandwake;
 2 
 3 /**
 4     包子鋪線程生產包子,吃貨線程消費包子。當包子沒有時(包子狀態爲false),吃貨線程等待,
 5     包子鋪線程生產包子 (即包子狀態爲true)
 6     並通知吃貨線程(解除吃貨的等待狀態),由於已經有包子了,那麼包子鋪線程進入等待狀態。
 7     接下來,吃貨線程可否進一步執行則取決於鎖的獲取狀況。
 8     若是吃貨獲取到鎖,那麼就執行吃包子動做,包子吃完(包 子狀態爲false),並通知包子鋪線程(解除包子鋪的等待狀態),
 9     吃貨線程進入等待。包子鋪線程可否進一步執行則取決於鎖的獲取狀況
10  */
11 public class BaoZi {
12     private String pi;
13     private String xian;
14     boolean flag = false;
15 
16     public BaoZi() {
17     }
18 
19     public BaoZi(String pi, String xian, boolean flag) {
20         this.pi = pi;
21         this.xian = xian;
22         this.flag = flag;
23     }
24 
25     public String getPi() {
26         return pi;
27     }
28 
29     public void setPi(String pi) {
30         this.pi = pi;
31     }
32 
33     public String getXian() {
34         return xian;
35     }
36 
37     public void setXian(String xian) {
38         this.xian = xian;
39     }
40 
41     public boolean isFlag() {
42         return flag;
43     }
44 
45     public void setFlag(boolean flag) {
46         this.flag = flag;
47     }
48 
49     @Override
50     public String toString() {
51         return "BaoZi{" +
52                 "pi='" + pi + '\'' +
53                 ", xian='" + xian + '\'' +
54                 ", flag=" + flag +
55                 '}';
56     }
57 }

包子鋪類

 1 package demosummary.waitingandwake;
 2 
 3 public class BaoZiPu extends Thread{
 4     private BaoZi bz;
 5 
 6     public BaoZiPu(String name, BaoZi bz) {
 7         super(name);
 8         this.bz = bz;
 9     }
10 
11     @Override
12     public void run() {
13         //定義一個變量來判斷作什麼皮和餡的包子
14         int count = 0;
15         while (true) {
16             synchronized (bz) {
17                 if (bz.flag == true) {//包子存在
18                     try {
19                         bz.wait();//進入等待狀態,既不須要作包子
20                     } catch (InterruptedException e) {
21                         e.printStackTrace();
22                     }
23                 }
24                 //沒有包子,包子鋪開始作包子
25                 System.out.println("包子鋪開始作包子");
26                 //判斷作什麼包子
27                 if (count != 0) {
28                     //作冰皮蛋黃包子
29                     bz.setPi("冰皮");
30                     bz.setXian("蛋黃");
31                 } else {
32                     //作薄皮豆沙餡
33                     bz.setPi("薄皮");
34                     bz.setXian("豆沙");
35                 }
36                 count++;
37                 //改變包子的狀態爲有包子
38                 bz.flag = true;
39                 System.out.println("包子作好了:"+bz.getPi()+bz.getXian()+"包子");
40                 System.out.println("請等待的顧客能夠來拿包子了");
41                 bz.notify();
42             }
43         }
44     }
45 }

顧客類

 1 package demosummary.waitingandwake;
 2 
 3 public class GuKe extends Thread{
 4     private BaoZi bz;
 5 
 6     public GuKe(String name, BaoZi bz) {
 7         super(name);
 8         this.bz = bz;
 9     }
10 
11     @Override
12     public void run() {
13         while (true) {
14             synchronized (bz) {
15                 if (bz.flag == false) {
16                     try {
17                         bz.wait();
18                     } catch (InterruptedException e) {
19                         e.printStackTrace();
20                     }
21                 }
22 
23                 System.out.println("顧客已拿到" + bz.getPi() + bz.getXian()+"包子");
24                 bz.flag = false;
25                 bz.notify();
26             }
27         }
28     }
29 }

測試類

 1 package demosummary.waitingandwake;
 2 
 3 public class Test {
 4     public static void main(String[] args) {
 5         //建立包子、包子鋪、顧客對象
 6         BaoZi baoZi = new BaoZi();
 7         BaoZiPu baoZiPu = new BaoZiPu("包子鋪", baoZi);
 8         GuKe guKe = new GuKe("顧客", baoZi);
 9         //調用包子鋪和顧客線程
10         baoZiPu.start();
11         guKe.start();
12     }
13 }
相關文章
相關標籤/搜索