2

我试图找出是否有可能有一个多生产者/多消费者队列,我可以在其中使用notify()而不是notifyAll(). 例如,在下面的实现中(来源:here)你不能简单地切换notifyAll()for notify()。为什么你不能切换并不完全清楚,所以我将把它作为一个预告片留给任何想帮助我理解这个问题的人。

所以下面的代码被破坏了:

public class BlockingQueue {

  private Object lock = new Object();

  private List queue = new LinkedList();
  private int  limit = 10;

  public BlockingQueue(int limit){
    this.limit = limit;
  }


  public void enqueue(Object item)
  throws InterruptedException  {
   synchronized(lock) {
    while(this.queue.size() == this.limit) {
      lock.wait();
    }
    if(this.queue.size() == 0) {
      lock.notify();
    }
    this.queue.add(item);
   }
  }


  public Object dequeue()
  throws InterruptedException{
   synchronized(lock) {
    while(this.queue.size() == 0){
      lock.wait();
    }
    if(this.queue.size() == this.limit){
      lock.notify();
    }

    return this.queue.remove(0);
  }
 }
}
4

3 回答 3

10

以下步骤导致我们陷入僵局。让我们将limit 设置为 1以保持示例简短。

  • E1 将项目排入队列。
  • E2 尝试入队 - 检查等待循环 - 已满 - 等待
  • E3 尝试入队 - 检查等待循环 - 已满 - 等待

  • D1 尝试出队 - 并且正在执行同步块

  • D2 尝试出队 - 进入(同步)块时的块 - 由于 D1
  • D3 尝试出队 - 进入(同步)块时的块 - 由于 D1

  • D1 正在执行入队 - 获取项目,调用通知,退出方法

  • 通知恰好唤醒了 E2(即“任何等待线程”)
  • 但是,D2 在 E2 之前进入同步块(E2 必须重新获得锁),因此 E2 在进入队列同步块时阻塞
  • D2 检查等待循环,队列中没有更多项目,所以等待
  • D3 在 D2 之后,但在 E2 之前进入块,检查等待循环,队列中没有更多项目,所以等待

  • 现在有 E3、D2 和 D3 等待!

  • 最后E2获取锁,入队,调用notify,exit方法

  • E2 的通知唤醒 E3(记住任何线程都可以被唤醒)

  • E3 检查等待循环条件,队列中已经有一个项目,所以等待。
  • 没有更多的线程调用通知和三个线程永久暂停!

解决方案:用 notifyAll 替换 notify

于 2012-12-08T08:47:19.990 回答
0

无论您的设计如何,都要知道notify()唤醒当前等待锁定对象的随机线程,并notifyAll()唤醒所有此类线程(以随机顺序)。

只要你的等待线程被编码来处理你给它们的唤醒类型,就不会有问题。

一个并不比另一个更好:在某些情况下notify()是最好的选择,在其他情况下notifyAll()- 这取决于正在解决的问题。

于 2012-12-08T05:51:56.873 回答
0

移出notify两种方法中的条件语句(不再需要条件),一切都应该正常工作。

而且,在原始来源中,方法是同步的。你为什么把这个改成synchronized(lock)? 只是忘记更改waitlock.wait()

于 2012-12-08T06:21:39.737 回答