多線程-生產者消費者
正解博客:https://blog.csdn.net/u011863767/article/details/59731447
永遠在循環(loop)裏調用 wait 和 notify,不是在 If 語句
現在你知道wait應該永遠在被synchronized的背景下和那個被多線程共享的對象上調用,下一個一定要記住的問題就是,你應該永遠在while循環,而不是if語句中調用wait。因為線程是在某些條件下等待的——在我們的例子裏,即“如果緩沖區隊列是滿的話,那麽生產者線程應該等待”,你可能直覺就會寫一個if語句。但if語句存在一些微妙的小問題,導致即使條件沒被滿足,你的線程你也有可能被錯誤地喚醒。所以如果你不在線程被喚醒後再次使用while循環檢查喚醒條件是否被滿足,你的程序就有可能會出錯——例如在緩沖區為滿的時候生產者繼續生成數據,或者緩沖區為空的時候消費者開始小號數據。所以記住,永遠在while循環而不是if語句中使用wait!我會推薦閱讀《Effective Java》,這是關於如何正確使用wait和notify的最好的參考資料。
有的這樣說:(http://www.tuicool.com/articles/a6ram23)
因為在多核處理器環境中, Signal 喚醒操作可能會激活多於一個線程(阻塞在條件變量上的線程),使得多個調用等待的線程返回。所以用while循環對condition多次判斷,可以避免這種假喚醒。
基於以上認知,下面這個是使用wait和notify函數的規範代碼模板:
1 2 3 4 5 6 7 8 |
// The standard idiom for calling the wait method in Java
synchronized (sharedObject) {
while (condition) {
sharedObject.wait();
// (Releases lock, and reacquires on wakeup)
}
// do action based upon condition e.g. take or put into queue
}
|
就像我之前說的一樣,在while循環裏使用wait的目的,是在線程被喚醒的前後都持續檢查條件是否被滿足。如果條件並未改變,wait被調用之前notify的喚醒通知就來了,那麽這個線程並不能保證被喚醒,有可能會導致死鎖問題。
註意:
1 永遠在synchronized的方法或對象裏使用wait、notify和notifyAll,不然Java虛擬機會生成 IllegalMonitorStateException。
2 永遠在while循環裏而不是if語句下使用wait。這樣,循環會在線程睡眠前後都檢查wait的條件,並在條件實際上並未改變的情況下處理喚醒通知。
3 永遠在多線程間共享的對象(在生產者消費者模型裏即緩沖區隊列)上使用wait。
生產者消費者代碼:
https://zhuanlan.zhihu.com/p/20300609 代碼有部分問題,修改如下
ProsumerToConsumer類
public class ProsumerToConsumer { public static void main(String[] args) throws Exception { Person person =new Person(); Thread t1=new Thread(new Producer(person),"生產者t1"); Thread t2=new Thread(new Producer(person),"生產者t2"); Thread t3=new Thread(new Producer(person),"生產者t3"); Thread s1=new Thread(new Consumer(person), "消費者s1"); Thread s2=new Thread(new Consumer(person), "消費者s2"); Thread s3=new Thread(new Consumer(person), "消費者s3"); s1.start(); s2.start(); Thread.sleep(2000); t1.start(); t2.start(); t3.start(); } }
producer代碼:
public class Producer implements Runnable { private Person person; // private String name; public Producer( Person person) { this.person=person; // this.name=name; } @Override public void run() { try { person.producer(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
consumer代碼:
public class Consumer implements Runnable{ private Person person; public Consumer( Person person) { this.person=person; } @Override public void run() { try { person.consumer(); } catch (Exception e) { e.printStackTrace(); } } }
person代碼:
public class Person { private static volatile int num=0; private Object obj= new Object(); private static final int MAX_NUM=5; public void producer()throws InterruptedException{ while(true){ synchronized (obj) { while(num==MAX_NUM) { System.out.println("box is full,size = " + num); obj.wait(); } num++; System.out.println( Thread.currentThread().getName()+ num); obj.notifyAll(); } } } public void consumer() throws InterruptedException{ while (true) { synchronized (obj) { while (num==0) { System.out.println("box is empty,size = " + num); obj.wait(); } num--; obj.notifyAll(); System.out.println(Thread.currentThread().getName() + num); } } } }
實例驗證1:如果判斷用的是while 數據在隊列容量範圍之內。
while(num==MAX_NUM)
while(true){ synchronized (obj) { while(num==MAX_NUM) { System.out.println("box is full,size = " + num); obj.wait(); }
實例驗證2:如果判斷用的是if ,數據已經超出了隊列的容量
if(num==MAX_NUM)
while(true){ synchronized (obj) { if(num==MAX_NUM) { System.out.println("box is full,size = " + num); obj.wait(); }
多線程-生產者消費者