2015-09-28 134 views
2

我想解決Java中使用多線程的生產者/消費者問題,但我一直陷入僵局,我無法弄清楚爲什麼。生產者/消費者死鎖多線程Java

BoundedBuffer.java

public class BoundedBuffer { 
    private final int[] buffer; 
    private final int N; 
    private int in = 0; 
    private int out = 0; 
    private int itemCount = 0; 

    public BoundedBuffer(int size) { 
     N = size + 1; 
     buffer = new int[N]; 
    } 

    public void insert(Producer producer, int item) { 
     synchronized (producer) { 
      while ((in + 1) % N == out) { 
       try { 
        producer.wait(); 
       } catch (InterruptedException e) {} 
     } 
     buffer[in] = item; 
     in = (in + 1) % N; 
     itemCount++; 
    } 

    public int remove(Consumer consumer) { 
     synchronized (consumer) { 
      while (in == out) { 
       try { 
        consumer.wait(); 
       } catch (InterruptedException e) {} 
      } 

      int item = buffer[out]; 
      buffer[out] = null; 
      out = (out + 1) % N; 
      itemCount--; 

      return item; 
     } 
    } 
} 

Producer.java

public class Producer extends Thread { 
    private int total = 0; 
    private BoundedBuffer buffer; 
    private int uniqueItem = 0; 

    public Producer(int total, BoundedBuffer b) { 
     this.total = total; 
     this.buffer = b; 
    } 

    public void run() { 
     for (int i = 0; i < quota; i++) { 
      try { 
       Thread.sleep((int)(Math.random() * 100)); 
      } catch (InterruptedException e) {} 
      buffer.insert(this, uniqueItem++); 
      this.notifyAll(); 
     } 
    } 
} 

Consumer.java

public class Consumer extends Thread { 
    private int total = 0; 
    private BoundedBuffer buffer; 

    public Consumer(int total, BoundedBuffer b) { 
     this.total = total; 
     this.buffer = b; 
    } 

    public void run() { 
     for (int i = 0; i < total; i++) { 
      try { 
       Thread.sleep((int)(Math.random() * 100)); 
      } catch (InterruptedException e) {}   
      buffer.remove(this); 
      this.notifyAll(); 
     } 
    } 
} 

該共德將運行一段時間,當我有調試設置(「這個製片人試圖插入這個項目...」)我看到一個很好的模式,但我偶爾會得到這個錯誤:

Exception in thread "Thread-2" java.lang.IllegalMonitorStateException 
    at java.lang.Object.notifyAll(Native Method) 
    at Consumer.run(Consumer.java:19) 

然後我有一段時間後發生死鎖。

我認爲我的問題是由於我正在同步的密鑰,producerconsumer實例。我不知道還有什麼可以同步的方法,那就是最讓我感動的東西。我相信我可以爲每個消費者/生產者分享一把鑰匙,但我不確定這將如何工作。

+0

爲什麼你認爲這是一個僵局? –

+0

@jameslarge https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem 不是建設性的評論btw。 – Zach

+0

@HongDuan我相信死鎖是由於我在生產者和消費者實例上進行同步而導致的,因此如果我在實例上等待(),沒有其他人能夠獲得鎖定。 – Zach

回答

3

您應該在BoundedBuffer本身上進行同步,並在同步塊中使用wait來釋放鎖定,並等待另一個線程通知睡眠的鎖定。

有本教程的完整範例: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

而且,請注意,您現在可以使用BlockingQueue以實現一個更簡單的方法,以消費者爲生產者圖案。

這裏你爲BlockingQueue文檔呈現出更簡單的消費者 - 生產者實施: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html

+0

這適用於多個生產者和消費者!我做的是'synchronize(this)',並在塊內調用'this.wait()',然後在該塊外調用'notifyAll()'。我也從Producer.java和Consumer.java中移除了'notifyAll()'。非常感謝你! – Zach

+0

我仍然有一個問題,就好像多個生產者在聲明'uniqueItem'靜態後創建具有相同ID的事件。我能做什麼? @eugenioy – Zach

+1

你可以使用'AtomicInteger'作爲'uniqueItem'並且執行'uniqueItem.inrementAndGet()'而不是'uniqueItem ++' – duckstep