1. 程式人生 > >多執行緒的等待喚醒機制之消費者和生產者模式

多執行緒的等待喚醒機制之消費者和生產者模式


/**
* 等待喚醒之生產者和消費者模型
* 生成者: 建立和新增資料的執行緒
* 消費者: 銷燬和刪除資料的執行緒
* 問題1: 生成者生成資料過快, 消費者消費資料相對慢,不接收資料了, 會造成資料丟失
* 問題2: 消費者消費資料過快, 生成者生成資料相對慢,不傳送資料了, 會造成資料被重複讀取
* 總結 : 生產資料過快會造成資料丟失
* 生成資料過慢會造成資料被重複讀取
*
*
* wait()和notifyAll()用法:
* 1. 必須是同一個物件的wait()和和notifyAll()方法
* 2. wait()和notifyAll()必須用在同步方法或同步程式碼塊中
* 3. wait()存在中斷和虛假喚醒(多個生成者和消費者執行緒等待時會出現虛假喚醒)問題,
* 虛假喚醒: 多個消費者執行緒等待時, 喚醒後, 都會接著執行同步方法中未完成的程式碼
* 解決辦法 : wait()必須放在while迴圈中 , 即線上程被喚醒後 先繼續進行判斷, 不符合條件就繼續等待`
*/

public class ProductorAndConsumerTest {

public static void main(String[] args) {
Clerk clerk = new Clerk();

Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "生成者A").start();
new Thread(consumer, "消費者B").start();
new Thread(productor, "生成者C").start();
new Thread(consumer, "消費者D").start();
}

}

// 店員 持有產品
class Clerk{
private int product = 0;

public synchronized void getPro() throws InterruptedException {
// wait()必須處在while迴圈體中
while (product >= 1){
System.out.println("產品已滿!");
this.wait();
}

System.out.println(Thread.currentThread().getName() + "生產了產品" + (++product));
this.notifyAll();

}

public synchronized void sale() throws InterruptedException {
// wait()必須處在while迴圈體中
while (product <= 0){
System.out.println(Thread.currentThread().getName() + "缺貨!");

this.wait();
}

System.out.println(Thread.currentThread().getName() + "消費了產品" + (--product));
this.notifyAll();

}
}

//生產者執行緒
class Productor implements Runnable{
private Clerk clerk;

public Productor(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
clerk.getPro();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

//消費者執行緒
class Consumer implements Runnable{
private Clerk clerk;

public Consumer(Clerk clerk) {
this.clerk = clerk;
}

@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
clerk.sale();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}