多執行緒通訊:生產者與消費者問題
阿新 • • 發佈:2021-01-17
1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。
2、wait()使當前執行緒阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步程式碼塊裡使用 wait()、notify/notifyAll() 方法。
3、 由於 wait()、notify/notifyAll() 在synchronized 程式碼塊執行,說明當前執行緒一定是獲取了鎖的。
當執行緒執行wait()方法時候,會釋放當前的鎖,然後讓出CPU,進入等待狀態。
只有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處於等待狀態的執行緒,然後繼續往下執行,直到執行完synchronized 程式碼塊的程式碼或是中途遇到wait() ,再次釋放鎖。
4、wait() 需要被try catch包圍,以便發生異常中斷也可以使wait等待的執行緒喚醒。
5、notify 和 notifyAll的區別
notify方法只喚醒一個等待(物件的)執行緒並使該執行緒開始執行。所以如果有多個執行緒等待一個物件,這個方法只會喚醒其中一個執行緒,選擇哪個執行緒取決於作業系統對多執行緒管理的實現。notifyAll 會喚醒所有等待(物件的)執行緒,儘管哪一個執行緒將會第一個處理取決於作業系統的實現。如果當前情況下有多個執行緒需要被喚醒,推薦使用notifyAll 方法。比如在生產者-消費者裡面的使用,每次都需要喚醒所有的消費者或是生產者,以判斷程式是否可以繼續往下執行。
package 生產者與消費者;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 先讓廚師生產完,然後喚醒消費者,阻塞廚師,消費者生產完喚醒廚師阻塞自己
public class Demo4 {
/**
* 多執行緒通訊問題, 生產者與消費者問題
*
* @param args
*/
public static void main(String[] args) {
Food f = new Food ();
new Cook(f).start();
new Waiter(f).start();
}
//廚師
static class Cook extends Thread {
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
f.setNameAndSaste("老乾媽小米粥", "香辣味");
} else {
f.setNameAndSaste("煎餅果子", "甜辣味");
}
}
}
}
//服務生
static class Waiter extends Thread {
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food {
private String name;
private String taste;
//true 表示可以生產
private boolean flag = true;
public synchronized void setNameAndSaste(String name, String taste) {
if (flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get() {
if (!flag) {
System.out.println("服務員端走的菜的名稱是:" + name + ",味道:" + taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}