我知道這個問題以前已經被問過和回答過很多次了,但是我只是無法弄清楚在互聯網上發現的例子,比如this或that之一。在java中實現你自己的阻塞隊列
這兩種解決方案都檢查阻塞隊列的數組/隊列/鏈表的空態爲notifyAll
等待線程在put()
方法中,反之亦然get()
方法。第二個鏈接中的comment強調了這種情況,並提到這不是必需的。
所以問題是,對我來說,檢查隊列是否爲空也似乎有點奇怪完整地通知所有等待的線程。有任何想法嗎?
在此先感謝。
我知道這個問題以前已經被問過和回答過很多次了,但是我只是無法弄清楚在互聯網上發現的例子,比如this或that之一。在java中實現你自己的阻塞隊列
這兩種解決方案都檢查阻塞隊列的數組/隊列/鏈表的空態爲notifyAll
等待線程在put()
方法中,反之亦然get()
方法。第二個鏈接中的comment強調了這種情況,並提到這不是必需的。
所以問題是,對我來說,檢查隊列是否爲空也似乎有點奇怪完整地通知所有等待的線程。有任何想法嗎?
在此先感謝。
我認爲邏輯上在notifyAll()
之前做額外檢查沒有任何危害。
一旦您從隊列中放入/獲取某些東西,您就可以簡單地使用notifyAll()
。一切仍然有效,而且你的代碼更短。但是,在調用notifyAll()
之前,檢查是否有人可能正在等待(通過檢查是否碰到隊列邊界)也沒有任何危害。這個額外的邏輯節省了不必要的notifyAll()
調用。
它只是取決於你想要一個更短,更乾淨的代碼,或者你希望你的代碼更有效地運行。 (沒有看過notifyAll()
的執行情況,如果沒有人在等待時是便宜的操作,無論如何性能增益可能並不明顯)
作者使用notifyAll()
的原因是簡單:他們不知道是否有必要,所以他們決定採用「更安全」的選擇。
在上述例子中這將是足夠的只是調用notify()
作爲每個單獨的元件加入,只有一個線程等待可以在所有情況下提供服務。
如果您的隊列也可以選擇像addAll(Collection<T> list)
這樣在一個步驟中添加多個元素,則會變得更加明顯,因爲在這種情況下,可以準確地提供多個等待空列表的線程:線程作爲元素被添加。
然而,notifyAll()
會在特殊的單元素情況下產生額外的開銷,因爲許多線程不必要地被喚醒,因此必須再次進入休眠狀態,同時阻止隊列訪問。所以用notify()
代替notifyAll()
可以在這種特殊情況下提高速度。
但是,然後不是使用等待/通知和所有同步,而是使用併發包會比任何智能等待/通知實現可以獲得更多的速度增加速度。
我知道這是迄今爲止的一個老問題,但在閱讀問題和答案之後,我無法幫助自己,我希望你覺得這很有用。
關於檢查如果隊列實際上是滿或空通知其他等待線程之前,你錯過了一些東西,是這兩種方法put (T t)
和T get()
都是方法,這意味着只有一個線程可以進入這些方法之一時間,但是這不會阻止他們在一起工作,所以如果一個線程,一個已經進入put (T t)
方法的另一個線程-b仍然可以進入並開始執行在T get()
方法的指令之前線程一個已經退出put (T t)
,所以這double-checking
設計是否會讓開發人員感覺更安全一些,因爲你無法知道未來的CPU上下文切換是否會發生。
更好和更推薦方法是使用Reentrant Locks
和Conditions
:
//使用這個方法有沒有必要double checking
我編輯的源代碼,從這個link
Condition isFullCondition;
Condition isEmptyCondition;
Lock lock;
public BQueue() {
this(Integer.MAX_VALUE);
}
public BQueue(int limit) {
this.limit = limit;
lock = new ReentrantLock();
isFullCondition = lock.newCondition();
isEmptyCondition = lock.newCondition();
}
public void put (T t) {
lock.lock();
try {
while (isFull()) {
try {
isFullCondition.await();
} catch (InterruptedException ex) {}
}
q.add(t);
isEmptyCondition.signalAll();
} finally {
lock.unlock();
}
}
public T get() {
T t = null;
lock.lock();
try {
while (isEmpty()) {
try {
isEmptyCondition.await();
} catch (InterruptedException ex) {}
}
t = q.poll();
isFullCondition.signalAll();
} finally {
lock.unlock();
}
return t;
}
,因爲lock
對象這兩種方法之間共享,這意味着只有一個線程a或b可以在不同於同步方法時其產生不同的顯示器,並且只有那些線程等待,因爲隊列已滿將被通知的wh輸入任意的這些方法中這裏有更多的空間,對於等待的線程也是如此,因爲隊列是空的,這將導致更好的CPU利用率。 你可以在源代碼中找到更詳細的例子here
代替isfullConditon,「isNotfullCondtion」將使其更具可讀性+1的實施 –
問題的一部分與在併發程序中正確使用wait和notify有關;引用Joshua Bloch的話說,他們就像「併發編程的低級(彙編語言)」。他主張使用'notifyAll',並且等待的線程在通知時應該總是在循環中進行檢查,以便它們可以根據需要繼續等待。實際上,根本不應該使用wait/notify,而是始終打算使用Java SE 5中提供的更高級別的併發API。而不是使用線程和等待/通知,根據任務和執行器設計併發應用程序。 – scottb
是的,我完全同意你的觀點,老東西但是好吃的東西:不需要重新發明輪子。但只是認爲我們需要使用wait和notifyAll來處理這種情況。我知道,等待()應該被用於循環中,在所有的Javadoc解釋(虛假喚醒):http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait(長)。但是通知的條件是什麼?這是我無法得到的。 – tugcem
參見[這裏](http://fuseyism.com/classpath/doc/java/util/concurrent/LinkedBlockingQueue-source.html) - 他們使用'ReentrantLock'和'Condition's,不'wait' /'notify'在所有。 – OldCurmudgeon