2015-09-28 59 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