1. 程式人生 > >多線程-生產者消費者

多線程-生產者消費者

CQ rgs tab 多核 .cn tag 範圍 模板 sleep

正解博客: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();
                }

技術分享圖片

多線程-生產者消費者