您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Java中等待喚醒機制線程通信的原理是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
線程間通信
概念:多個線程在處理同一個資源,但是處理的動作(線程的任務)卻不相同。比如:線程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 上的全部線程。
注意:
哪怕只通知了一個等待的線程,被通知線程也不能立即恢復執行,因為它當初中斷的地方是在同步塊內,而此刻它已經不持有鎖,所以她需要再次嘗試去獲取鎖(很可能面臨其它線程的競爭),成功后才能在當初調用 wait 方法之后的地方恢復執行。
總結如下:
如果能獲取鎖,線程就從 WAITING 狀態變成 RUNNABLE 狀態;否則,從 wait set 出來,又進入 entry set,線程就從 WAITING 狀態又變成 BLOCKED 狀態調用wait和notify方法需要注意的細節
wait方法與notify方法必須要由同一個鎖對象調用。因為:對應的鎖對象可以通過notify喚醒使用同一個鎖對象調用的wait方法后的線程。
wait方法與notify方法是屬于Object類的方法的。因為:鎖對象可以是任意對象,而任意對象的所屬類都是繼承了Object類的。
wait方法與notify方法必須要在同步代碼塊或者是同步函數中使用。因為:必須要通過鎖對象調用這2個方法。
生產者與消費者問題
等待喚醒機制其實就是經典的“生產者與消費者”的問題。就拿生產包子消費包子來說等待喚醒機制如何有效利用資源:
/* 包子鋪線程生產包子,吃貨線程消費包子。當包子沒有時(包子狀態為false),吃貨線程等待,包子鋪線程生產包子 (即包子狀態為true),并通知吃貨線程(解除吃貨的等待狀態),因為已經有包子了,那么包子鋪線程進入等待狀態。 接下來,吃貨線程能否進一步執行則取決于鎖的獲取情況。如果吃貨獲取到鎖,那么就執行吃包子動作,包子吃完(包 子狀態為false),并通知包子鋪線程(解除包子鋪的等待狀態),吃貨線程進入等待。包子鋪線程能否進一步執行則取 決于鎖的獲取情況 */
代碼實現
包子類
package demo01; public class BaoZi { String pier; String xianer; boolean flag = false;//包子資源 是否存在 包子資源狀態 }
吃貨線程類:
package demo01; 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(); } } } }
包子鋪線程類:
package demo01; 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(); } } } }
測試類:
package demo01; 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(); } }
以上就是Java中等待喚醒機制線程通信的原理是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。