線程間通訊多線程
概念:多個線程在處理同一個資源,可是處理的動做(線程的任務)卻不相同。併發
好比:線程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 }