多執行緒使用wait和notify做生產者消費者模型導致執行緒全部假死
阿新 • • 發佈:2022-04-29
分析假死的原因:
首先我們每次只生產一個數據,然後消費者進行消費,
public class Value {
public static String value = "";//這個值作為生產消費的容器
}
生產者端程式碼:
private String lock; public Producer (String lock) { super(); this.lock = lock; } public void setValue() { try { synchronized (lock) { while (!"".equals(Value.value)) { System.out.println("生產者:"+Thread.currentThread().getName()+"等待"); lock.wait(); } System.out.println("生產者:"+Thread.currentThread().getName()+"開始工作了"); String value = "Producer"; System.out.println("set value :"+value); Value.value = value; lock.notify(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
消費者端
private String lock; public Consumer(String lock) { this.lock = lock; } public void getValue() { try { synchronized (lock) { if ("".equals(Value.value)) { System.out.println("消費者:"+Thread.currentThread().getName()+"等待"); lock.wait(); } System.out.println("消費者:"+Thread.currentThread().getName()+"開始消費了"); System.out.println("get value :"+Value.value ); Value.value = ""; lock.notify(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
public class MyThread1 extends Thread {
private Producer p;
public MyThread1(Producer p) {
this.p = p;
}
@Override
public void run() {
while(true){
p.setValue();//此執行緒不停的生產
}
}
package com.demo.thread; public class MyThread2 extends Thread{ private Consumer c; public MyThread2(Consumer c) { this.c = c; } @Override public void run() { while (true) { c.getValue();//此執行緒不聽的消費 } } }
測試:
String lock = new String("lock");
Producer p = new Producer (lock);
Consumer c = new Consumer (lock);
MyThread1[] t1 = new MyThread1[2];
MyThread2 [] t2 = new MyThread2[2];
for(int i = 0 ;i<2;i++){
t1[i] = new MyThread1(p);
t1[i].setName("生產者:"+(i+1));
t2[i] = new MyThread2(c);
t2[i].setName("消費者:"+(i+1));
t1[i].start();
t2[i].start();
}
Thread.sleep(3000);
Thread[] tArr = new Thread [Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(tArr);
for(int i = 0 ;i<tArr.length;i++){
System.out.println(tArr[i].getName()+"t"+tArr[i].getState());
}
擷取控制檯程式碼部分程式碼: 分析:因為notify每次只喚醒一個執行緒,因此並不確定他喚醒的是哪一個執行緒,所以消費1喚醒的是消費者2,此時剛好沒有資料被生產,消費者2也進入等待,並喚醒生產者2,生產者2生產完資料之後進入wait同時喚醒執行緒,此時喚醒的是生產者1 ,因為資料不為空,因此兩生產者都進入等待狀態,此時四個執行緒全部wait,即假死狀態,結果如控制檯所示. 解決方式:這裡每次執行完畢之後應該喚醒所有執行緒即可.
生產者:生產者:2開始工作了
set value :Producer
消費者:消費者:1開始消費了
get value :Producer
消費者:消費者:1等待
生產者:生產者:2開始工作了
set value :Producer
生產者:生產者:2等待
生產者:生產者:1等待
消費者:消費者:2開始消費了
get value :Producer
消費者:消費者:2等待
生產者:生產者:2開始工作了
set value :Producer
生產者:生產者:2等待
生產者:生產者:1等待
消費者:消費者:1開始消費了
get value :Producer
消費者:消費者:1等待//消費者1等待,喚醒消費者2
消費者:消費者:2開始消費了
get value :
消費者:消費者:2等待//消費者2喚醒生產者2
生產者:生產者:2開始工作了
set value :Producer
生產者:生產者:2等待//從這裡開始,生產者2喚醒1,兩者最後都進入wait.
生產者:生產者:1等待
main RUNNABLE
生產者:1 WAITING
消費者:1 WAITING
生產者:2 WAITING
消費者:2 WAITING