Java concurrent併發隊列指南
1.概述
在本教程中,我們將介紹Java中並發隊列的一些主要實現。有關隊列的一般介紹,請參閱我們的Java隊列接口指南一文。
2.隊列
在多線程應用程序中,隊列需要處理多個並發的生產者-消費者方案。正確選擇並發隊列對於在我們的算法中實現良好性能至關重要。
首先,我們將看到阻塞隊列和非阻塞隊列之間的一些重要區別。然後,我們將看一些實現和最佳實踐。
2.阻塞與非阻塞隊列
BlockingQueue提供了一種簡單的線程安全機制。在此隊列中,線程需要等待隊列的可用性。生產者將在添加元素之前等待可用容量,而消費者將等待直到隊列為空。在這些情況下,非阻塞隊列將拋出異常或返回特殊值,例如null或false 。
為了實現這種阻塞機制, BlockingQueue接口在常規Queue函數的基礎上提供了兩個函數: put和take 。這些功能等效於標準Queue中的add和remove 。
3.並發隊列實現
3.1。 ArrayBlockingQueue
顧名思義,此隊列在內部使用數組。因此,它是一個有界隊列,這意味著它具有固定的大小。
一個簡單的工作隊列就是一個示例用例。這種情況通常是生產者/消費者比率低的情況,我們將耗時的任務分配給多個工人。由於此隊列不能無限增長,因此如果出現內存問題,大小限制將作為安全閾值。
說到內存,重要的是要注意隊列已預先分配了陣列。雖然這可以提高吞吐量,但也可能消耗比必要更多的內存。例如,大容量隊列可能長時間保持空置。
另外, ArrayBlockingQueue對放置和獲取操作都使用一個鎖。這樣可以確保不覆蓋條目,但會降低性能。
3.2。 LinkedBlockingQueue
LinkedBlockingQueue使用LinkedList變體,其中每個隊列項都是一個新節點。雖然這使隊列在原則上不受限制,但仍然具有Integer.MAX_VALUE的硬限制。
另一方面,我們可以使用構造函數LinkedBlockingQueue(int capacity)設置隊列大小。
該隊列使用不同的鎖進行放置和取出操作。結果,這兩個操作可以並行完成並提高了吞吐量。
由於LinkedBlockingQueue可以是有界的或無界的,為什麼我們要在此之上使用ArrayBlockingQueue ?每次從隊列中添加或刪除項目時, LinkedBlockingQueue都需要分配和取消分配節點。因此,如果隊列快速增長和快速收縮,則ArrayBlockingQueue可能是更好的選擇。
據說LinkedBlockingQueue的性能是不可預測的。換句話說,我們始終需要剖析我們的方案以確保我們使用正確的數據結構。
3.3。 PriorityBlockingQueue
當我們需要按特定順序消費商品時, PriorityBlockingQueue是我們的首選解決方案。為此, PriorityBlockingQueue使用基於數組的二進制堆。
雖然在內部使用單個鎖定機制,但是獲取操作可以與放置操作同時進行。使用簡單的自旋鎖可以實現這一點。
典型的用例是使用具有不同優先級的任務。我們不希望低優先級的任務代替高優先級的任務。
3.4。延遲隊列
當使用者只能購買過期的物品時,我們使用DelayQueue 。有趣的是,它在內部使用PriorityQueue來按項目的到期時間對其進行排序。
由於這不是通用隊列,因此它無法涵蓋ArrayBlockingQueue或LinkedBlockingQueue這麼多的場景。例如,我們可以使用此隊列來實現一個簡單的事件循環,類似於在NodeJS中找到的事件循環。我們將異步任務放在隊列中,以便在它們到期時進行後續處理。
3.5。 LinkedTransferQueue
LinkedTransferQueue引入了一種傳輸方法。儘管其他隊列通常在生產或消費商品時阻塞,但LinkedTransferQueue**允許生產者等待商品的消費**。
當我們需要保證放入隊列中的某個特定項目已被某人拿走時,可以使用LinkedTransferQueue 。同樣,我們可以使用此隊列實現簡單的反壓算法。實際上,通過阻止生產者直到消費,消費者可以驅動所產生的消息流。
3.6。同步隊列
儘管隊列通常包含許多項目,但SynchronousQueue最多始終只有一個項目。換句話說,我們需要將SynchronousQueue視為在兩個線程之間交換某些數據的簡單方法。
當我們有兩個需要訪問共享狀態的線程時,我們通常將它們與CountDownLatch或其他同步機制同步。通過使用SynchronousQueue ,我們可以避免線程的這種手動同步。
3.7。並發鏈接隊列
ConcurrentLinkedQueue是本指南中唯一的非阻塞隊列。因此,它提供了一種“免等待”算法,其中**add和poll保證是線程安全的,並立即返回**。該隊列使用CAS(比較和交換)代替鎖。
在內部,它基於Maged M. Michael和Michael L. Scott的簡單,快速和實用的非阻塞和阻塞並發隊列算法。
對於經常禁止使用阻塞數據結構的現代反應系統,它是一個理想的選擇。
另一方面,如果我們的消費者最終在循環中等待,我們應該選擇阻塞隊列作為更好的選擇。
4。結論
在本指南中,我們遍歷了不同的並發隊列實現,並討論了它們的優缺點。考慮到這一點,我們有更好的能力來開發高效,持久和可用的系統。