2015-02-24 111 views
2

我正在從java documentation正在學習線程同步。我實施了一個着名的問題生產者 - 消費者問題。但它沒有按預期給出結果。我在HERE,HERE,HERE,HERE,HERE和一些其他堆棧交換站點和非堆棧交換站點上搜索了很多關於這個問題,但無法解決我的問題。這裏是我的代碼:生產者消費者沒有給出想要的結果

GetSetItem.java

public class GetSetItem { 

    private volatile boolean available = false; 

    private int item; 

    public synchronized void set(int item) { 
     while(available) { 
     try { 
      wait(); 
     } catch (InterruptedException ie) { 
      System.err.println("Interrupted: " + ie.getMessage()); 
     } 
     } 

     this.item = item; 
     available = true; 
     notifyAll(); 
    } 

    public synchronized int get() { 
     while(!available) { 
     try { 
      wait(); 
     } catch (InterruptedException ie) { 
      System.err.println("Interrupted: " + ie.getMessage()); 
     } 
     } 
     available = false; 
     notifyAll(); 
     return item; 
    } 

} 

Consumer.java

public class Consumer implements Runnable { 

    private int number; // Just for show #1,#2 etc. For future use 
    private GetSetItem consumer; 

    public Consumer(GetSetItem item, int seq) { 
     consumer = item; 
     number = seq; 
    } 

    @Override 
    public void run() { 
     int value = -1; 

     for(int i = 0; i < 10; i++) { 
     value = consumer.get(); 
     System.out.println("Consumer #" + number + " get: " + value); 
     } 

    } 
} 

Producer.java

public class Producer implements Runnable { 

    private GetSetItem producer; 

    private int number = 0; 

    public Producer(GetSetItem item, int seq) { 
     producer = item; 
     number = seq; 
    } 

    @Override 
    public void run() { 
     for(int i = 0; i < 10; i++) { 
     producer.set(i); 
     System.out.println("Producer #" + number + " Put: " + i); 
     } 
    } 

} 

ProducerConsumerMain.java

public class ProducerConsumerMain { 

    public static void main(String[] args) { 

     GetSetItem item = new GetSetItem(); 

     Producer p = new Producer(item, 1); 
     Consumer c = new Consumer(item, 1); 

     new Thread(p).start(); 
     new Thread(c).start(); 

    } 

} 

輸出是:

Consumer #1 get: 0 
Producer #1 Put: 0 
Producer #1 Put: 1 
Consumer #1 get: 1 
Producer #1 Put: 2 
Consumer #1 get: 2 
Producer #1 Put: 3 
Consumer #1 get: 3 
Producer #1 Put: 4 
Producer #1 Put: 5 
Consumer #1 get: 4 
Consumer #1 get: 5 
Producer #1 Put: 6 
Producer #1 Put: 7 
Consumer #1 get: 6 
Consumer #1 get: 7 
Consumer #1 get: 8 
Producer #1 Put: 8 
Producer #1 Put: 9 
Consumer #1 get: 9 

但產量應該在生產 - >消費格式。這意味着消費者只有在生產商可以購買和生產的情況下才能消費該產品。我也試過private boolean available = false而不是private volatile boolean available = false;,但沒有收到預期的輸出。

那麼請告訴我我在做什麼錯,以及如何成功實現這個問題。

回答

1

我通過使用GetSetItemget()set()方法System.out.println(...)聲明,並從相應的producerconsumer類刪除System.out.println(...)解決了這個問題。正如:
的get()的方法 GetSetItem.java

public synchronized void set(int item, int number) { 
    while(available) { 
     try { 
     wait(); 
     } catch (InterruptedException ie) { 
     System.err.println("Interrupted: " + ie.getMessage()); 
     } 
    } 

    this.item = item; 


    /* Putting this line here gives expected output because this 
    * statement is synchronized due to method synchronization. 
    */ 
     System.out.println("Producer #" + number + " Produced: " + item);    

     available = true; 
     notifyAll(); 
} 

set()方法的GetSetItem。java的

public synchronized int get(int number) { 
    while (!available) { 
     try { 
     wait(); 
     } catch (InterruptedException ie) { 
     System.err.println("Interrupted: " + ie.getMessage()); 
     } 
    } 
    /* 
    * Putting this line here gives expected output because this statement 
    * is synchronized due to method synchronization. 
    */ 


    System.out.println("Consumer #" + number + " Consumed: " + item); 


    available = false; 
    notifyAll(); 
    return item; 
} 

編輯:輸出:

Producer #1 Produced: 0 
Consumer #1 Consumed: 0 
Producer #1 Produced: 1 
Consumer #1 Consumed: 1 
Producer #1 Produced: 2 
Consumer #1 Consumed: 2 
Producer #1 Produced: 3 
Consumer #1 Consumed: 3 
Producer #1 Produced: 4 
Consumer #1 Consumed: 4 
Producer #1 Produced: 5 
Consumer #1 Consumed: 5 
Producer #1 Produced: 6 
Consumer #1 Consumed: 6 
Producer #1 Produced: 7 
Consumer #1 Consumed: 7 
Producer #1 Produced: 8 
Consumer #1 Consumed: 8 
Producer #1 Produced: 9 
Consumer #1 Consumed: 9 
1

你的代碼看起來很好,最可能的問題是System.out不是線程安全的。你會想你的println()通話以及同步:

@Override 
public void run() { 
    for (int i = 0; i < 10; i++) { 
     producer.set(i); 
     synchronized (System.out) { 
      System.out.println("Producer #" + number + " Put: " + i); 
     } 
    } 
} 

然後,出去放將類似於:

Producer #1 Put: 0 
Producer #1 Put: 1 
Consumer #1 get: 0 
Consumer #1 get: 1 
Producer #1 Put: 2 
Producer #1 Put: 3 
Consumer #1 get: 2 
Consumer #1 get: 3 
Producer #1 Put: 4 
Consumer #1 get: 4 
Producer #1 Put: 5 
Producer #1 Put: 6 
Consumer #1 get: 5 
Consumer #1 get: 6 
Consumer #1 get: 7 <<<< 
Producer #1 Put: 7 <<<< 
Producer #1 Put: 8 
Consumer #1 get: 8 
Consumer #1 get: 9 <<<< 
Producer #1 Put: 9 <<<< 

它仍然有可能你的線程將get/setprintln語句之間被暫停在這種情況下,您的消費者看起來好像消費了尚未製作的東西,就像我在上述輸出中指出的那樣。這只是輸出的問題,但是,你的代碼運行良好並且執行它應該做的事情。

+0

我可以用 「如果(可用)」 語句,而不是 「而(可用)」 沒有,因爲我研究了「等待改變的行爲( )「方法將阻止對象,直到」notify()或notifyAll()「被調用。 – 2015-02-24 11:36:04

+1

阻止是你想要達到的目標:你的'消費者'必須等到'生產者'說完成,然後'生產者'不能產生任何新的東西,直到價值被消耗完。在這種情況下你可以使用'if',但是如果你有多個'Producer'和'Consumer's,那麼你會遇到麻煩。最好使用'notify()',因爲它只喚醒一個線程。那麼,使用'if'可能是安全的,但它並不重要。 – DennisW 2015-02-24 11:40:40