2012-03-23 143 views
0

我最近看到下面的实现排队的一个BlockingQueue的 (sourcewhile循环在Java中的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)

被替换看起来方法入队是同步的,因此一次只能有一个线程在方法体中执行,并调用wait()。线程一旦收到通知,无法再继续检查this.queue.size() == this.limit条件继续前进吗?

+0

谢谢。后续问题:为什么在添加项目之前排队调用notifyAll()?这不会导致线程等待出队以检查空队列吗?首先添加项目然后notifyAll会更好吗?像这样:queue.add(item); if(queue.size()== 1){notifyAll();} – morfys 2012-03-26 08:43:04

+0

恕我直言,关闭add(item)和notifyAll()的顺序并不重要,因为方法enqueue是同步的。因此add(item)和notifyAll()调用都必须在另一个线程可以执行之前完成。 – morfys 2012-03-26 09:06:21

回答

4

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

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

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

其实,他们都不会;这些线程可能已经被notifyAll唤醒,出于某种完全不相关的原因。

5

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

线程还可以唤醒没有被通知,中断或超时,所谓的虚假唤醒。虽然这在实践中很少会发生,但应用程序必须通过测试应该引起线程被唤醒的条件来防范它,并且在条件不满足时继续等待。换句话说,等待应始终发生在循环中,如下所示:

synchronized (obj) { 
    while (<condition does not hold>) 
     obj.wait(timeout); 
    ... // Perform action appropriate to condition 
}