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

chr*_*tek 2 java queue multithreading blockingqueue data-structures

我试图找出是否有可能有一个多生产者/多个消费者队列,我可以使用notify()而不是notifyAll().例如,在下面的实现中(源代码:这里),你不能只是简单地切换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);
  }
 }
}
Run Code Online (Sandbox Code Playgroud)

xag*_*gyg 10

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

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

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

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

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

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

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

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

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

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

SOLUTION:用notifyAll替换notify