notify()而不是notifyAll()用于阻塞队列

时间:2012-12-08 05:27:28

标签: java multithreading data-structures queue blockingqueue

我试图找出是否可以使用多个生产者/多个消费者队列,我可以使用notify()而不是notifyAll()。例如,在下面的实现中(来源:here),您不能只为notifyAll()切换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);
  }
 }
}

3 个答案:

答案 0 :(得分:10)

以下步骤导致我们陷入僵局。我们将限制为1 以保持示例简短。

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

  • D1尝试出列 - 并且正在执行synchronized块

  • D2尝试出列 - 在进入(同步)块时阻塞 - 由于D1
  • D3尝试出列 - 阻止进入(同步)块 - 由于D1

  • D1正在执行enqueue - 获取项目,调用notify,退出方法

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

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

  • 最后E2获取锁定,排队项目,调用通知,退出方法

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

  • E3检查等待循环条件,队列中已有一个项目,所以等待。
  • 没有更多的线索要求通知和三条线路永久悬挂!

解决方案:将notify替换为notifyAll

答案 1 :(得分:0)

无论您的设计是什么,都知道notify()唤醒当前正在等待锁对象的随机线程,notifyAll()唤醒所有这样的线程(以随机顺序)。

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

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

答案 2 :(得分:0)

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

并且,在原始源中,方法是同步的。你为什么把它改为synchronized(lock)?只是忘记将wait更改为lock.wait()