2012-03-23 45 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 
}