0

我最近看到了 BlockingQueue 的以下入队实现(来源

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

为什么while循环是必要的,并且可以while替换为if (this.queue.size() == this.limit)

似乎方法入队是同步的,因此一次只能在方法主体中执行 1 个线程并调用wait(). 一旦线程被通知,它就不能继续前进而不this.queue.size() == this.limit再次检查条件吗?

4

2 回答 2

5

上的文档Object.wait()解释得最好:

线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,如果条件不满足则继续等待。换句话说,等待应该总是在循环中发生,就像这样:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }
于 2012-03-23T12:59:23.887 回答
4

不,您需要 while,因为可能有多个线程在等待队列中的空间打开,并且 notifyAll() 调用将唤醒所有线程。

wait() 方法实际上释放了同步监视器,以便其他线程可以取得进展。如果没有,那么任何试图从队列中删除内容的线程也会卡住等待进入 get() 方法中的同步块(例如)。

只有一个等待线程会看到部分空的队列。

实际上,没有一个可能;由于某些完全不相关的原因,线程可能已被 notifyAll 唤醒。

于 2012-03-23T12:47:15.717 回答