Java SE多執行緒部分--20.等待喚醒機制
阿新 • • 發佈:2018-12-12
1、概述
等待喚醒機制就是用於解決執行緒間通訊的問題、常見的方法如下:
1. wait:執行緒不再活動,不再參與排程,進入 wait set 中,因此不會浪費 CPU 資源,也不會去競爭鎖了, 這時的執行緒狀態即是 WAITING。它還要執行一個特別的動作,也即是“通知(notify)” 在這個物件上等待的執行緒從wait set 中釋放出來,重新進入到排程佇列(ready queue)中 2. notify:則選取所通知物件的 wait set 中的一個執行緒釋放;例如,餐館有空位置後, 等候就餐最久的顧客最先入座。 3. notifyAll:則釋放所通知物件的 wait set 上的全部執行緒。 注意細節: 1. wait方法與notify方法必須要由同一個鎖物件呼叫。因為:對應的鎖物件可以通過notify喚醒使用同一個鎖物件呼叫的wait方法後的執行緒。 2. wait方法與notify方法是屬於Object類的方法的。因為:鎖物件可以是任意物件,而任意物件的所屬類都是繼承了Object類的。 3. wait方法與notify方法必須要在同步程式碼塊或者是同步函式中使用。因為:必須要通過鎖物件呼叫這2個方法。
下面來說下生產者和消費者的問題 :
執行緒A用來生產了一產品,執行緒B用來消費了一個產品,產品可以理解為同一資源,執行緒A與執行緒B處理的動作,
一個是生產,一個是消費,那麼執行緒A與執行緒B之間就要用到等待喚醒機制。
2、例項
例子 :以包子為例
生產執行緒已經生產完畢包子了, 此時, 不再生產, 而是等待消費執行緒來消費. 如果生產執行緒沒有包子, 此時, 就需要生產, 生產完畢後, 喚醒消費執行緒來消費.
消費執行緒消費完畢包子後, 不再消費, 此時, 消費執行緒就需要喚醒生產執行緒來生產. 如果消費執行緒沒有包子, 消費執行緒就進入到等待狀態.
共享資源類 :
public class BaoZi { // 屬性 private String pier; private String xianer; // 型別 boolean type = false; boolean flag = false; public BaoZi(String pier, String xianer) { this.pier = pier; this.xianer = xianer; } public BaoZi() { } @Override public String toString() { return "BaoZi{" + "pier='" + pier + '\'' + ", xianer='" + xianer + '\'' + '}'; } public String getPier() { return pier; } public void setPier(String pier) { this.pier = pier; } public String getXianer() { return xianer; } public void setXianer(String xianer) { this.xianer = xianer; } }
生產執行緒 :
public class BaoZiPu extends Thread {
// 屬性 (記錄資料)
private BaoZi baoZi;
// 構造方法
public BaoZiPu(BaoZi baoZi, String name) {
super(name);
this.baoZi = baoZi;
}
// 重寫 Thread 類的 run 方法
@Override
public void run() {
// 不斷生產
while (true) {
// 同步鎖環境
synchronized (baoZi) {
// 生產功能
produce();
}
}
}
// 生產功能 : 子執行緒
public void produce() {
// 隨機時間切換 : 0 ~ 500 毫秒 (long)(Math.random() * 500)
/*try {
Thread.sleep((long)(Math.random() * 500));
} catch (InterruptedException e) {
e.printStackTrace();
}*/
// 1. 有包子, 生產執行緒進入等待
if (baoZi.flag == true) {
try {
baoZi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 2. 沒有包子, 生產執行緒進行生產
// 2.1 判斷資料型別, 賦值不同的資料
if (baoZi.type == false) {
// 大蔥牛肉
baoZi.setPier("大蔥");
baoZi.setXianer("牛肉");
} else {
// 金牌蝦仁
baoZi.setPier("金牌");
baoZi.setXianer("蝦仁");
}
// 2.2 更改型別 (型別取反即可)
baoZi.type = !baoZi.type;
// 2.3 輸出語句
System.out.println(Thread.currentThread().getName() + " 生產了" + baoZi.getPier() + baoZi.getXianer() + " 的包子.");
// 2.4 更改包子的標記
baoZi.flag = true;
// 3. 喚醒消費執行緒
baoZi.notify();
}
}
消費執行緒 :
public class ChiHuo extends Thread {
// 屬性 (記錄資料)
private BaoZi baoZi;
// 構造方法
public ChiHuo(BaoZi baoZi, String name) {
super(name);
this.baoZi = baoZi;
}
// 重寫 Thread 類的 run 方法
@Override
public void run() {
// 不斷消費
while (true) {
synchronized (baoZi) {
// 消費功能
consume();
}
}
}
// 消費功能
public void consume() {
// 隨機時間切換 : 0 ~ 500 毫秒 (long)(Math.random() * 500)
/*try {
Thread.sleep((long)(Math.random() * 500));
} catch (InterruptedException e) {
e.printStackTrace();
}*/
// 1. 判斷, 沒有包子, 死等
if (baoZi.flag == false) {
try {
baoZi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 2.1 取出資料, 消費
System.out.println(Thread.currentThread().getName() + " 正在吃 " + baoZi.getPier() + baoZi.getXianer() + " 的包子.");
// 2.2 將資料清空
baoZi.setPier("null");
baoZi.setXianer("null");
// 2.3 修改包子的標記
baoZi.flag = false;
// 3. 喚醒生產執行緒生產包子
baoZi.notify();
}
}
測試類 :
public class ProduceAndConsumeTest {
public static void main(String[] args) {
// 準備一個共享資料類
BaoZi baoZi = new BaoZi();
// 生產執行緒
BaoZiPu baoZiPu = new BaoZiPu(baoZi, "生產執行緒");
// 消費執行緒
ChiHuo chiHuo = new ChiHuo(baoZi, "消費執行緒");
// 啟動執行緒
baoZiPu.start();
chiHuo.start();
}
}