JUC干貨之六大阻塞隊列BlockingQueue
摘要:你如果還不了解Java 21中BlockingQueue的六大阻塞隊列,那么看這篇文章就夠了。我會介紹阻塞隊列的定義、種類、實現原理以及應用。
JUC干貨系列目錄:
- JAVA JUC干貨之線程池狀態和狀態切換
- JAVA JUC干貨之線程池實現原理和源碼詳解(上)
- JAVA JUC干貨之線程池實現原理和源碼詳解(下)
- JAVA JUC干貨之六大阻塞隊列BlockingQueue
綜述
??大家如果看完上述【JAVA JUC干貨系列】文章,相信對線程池的理解會更絲滑流暢,例如java中的線程池由下列四個核心組件組成——線程池管理器ThreadPoolExecutor、用作緩沖區的任務隊列、創建新線程的線程工廠和拒絕策略等。本文就在上述博客的基礎上,分享線程池核心組件任務隊列的基本概念、常用種類、實現原理以及應用。肝文不易,看完別忘了點贊關注哦。
??在多線程編程領域,所謂阻塞,是指在某些情況下會掛起線程,一旦條件滿足,被掛起的線程又會自動被喚醒。阻塞隊列中的“阻塞”也是這個意思。
??熟悉消息隊列(MessageQueue)八股文的老鐵一定知道消息隊列有解耦、異步處理、提高系統可擴展性和削峰填谷神奇效果。同樣阻塞隊列BlockingQueue的作用也包含這四種,區別是BlockingQueue只作用于本機器,而消息隊列相當于分布式BlockingQueue。
??為什么需要阻塞隊列?它在消息傳輸過程中充當臨時保存消息的容器,是實現生產者-消費者模型等常見并發模式的重要工具。當系統中出現“生產”和“消費”的速度不一致或穩定性等影響系統健壯性因素的時候,就需要阻塞隊列削峰填谷,作為抽象層,能夠有效地平衡生產者和消費者之間的速度差異,提供一種平滑和安全的數據交互方式。
??Java 中的線程池使用了兩種類型的任務隊列:有界隊列和無界隊列。有界隊列可以限制任務隊列的最大長度,控制待處理任務的數量;而無界隊列則沒有長度限制,可以永無止境地向其提交新任務。
BlockingQueue核心操作方法
??本章節介紹阻塞隊列常用的方法及其行為。
- remove():移除隊首元素,若移除成功,則返回true;如果移除失敗(隊列為空),則會拋出異常。
- element():返回隊列頭部元素但不移除,如果隊列為空,拋出異常。
- peek():返回隊列頭部元素但不移除,如果隊列為空,返回 null。
下面根據插入和獲取對操作方法進行分類介紹。
插入數據
??add(E e) 向隊列尾部寫入新的數據e,如果插入成功,則返回true;如果隊列已滿則插入失敗,拋出隊列已滿的異常。
??offer(E e):表示如果可能的話,將新數據寫入隊列尾部,即如果BlockingQueue有空間,則插入成功并返回true;否則,因隊列已滿而插入失敗,返回false。本方法不阻塞當前執行方法的線程。
??offer(E e, long timeout, TimeUnit unit):添加元素到隊列中,如果隊列滿了返回 false。可以設定等待插入元素的時間,如果在指定的時間內還不能加入,則拋出IllegalStateException(“Queue full”)異常。
??put(E e):添加元素到隊列中,如果隊列滿了則調用此方法的線程被掛起,直到隊列有空間再繼續。
獲取數據
??poll():從隊列中移除并獲取位于隊首的元素,若成功,則返回隊首元素;如果隊列為空則返回 null。
??poll(long time):取走排在隊列首位的對象。若隊列為空不能立即取出元素,則可以等待time參數規定的時間, 超時仍然取不到時返回null;否則,返回取得的元素。
??poll(long timeout, TimeUnit unit):從隊列頭部獲取數據并且該數據會從隊列頭部移除,如果隊列為空則當前線程會阻塞指定的時間,直到在此期間有新的數據寫入,或者阻塞的當前線程被其它線程中斷,當線程由于超時退出阻塞時,返回值為null。
??take():從隊列頭部獲取排在首位的對象并且該數據會從隊列頭部移除,如果隊列沒有任何元素則阻塞,直到隊列中有元素被加入。
??drainTo():一次性從BlockingQueue獲取所有可用的數據對象(還可以指定獲取數據的個數),
通過該方法,可以提升獲取數據效率;不需要多次分批加鎖或釋放鎖。
??peek():獲取隊首元素,若成功,則返回隊首元素;否則,返回null。它只查看但不移除隊列中的元素。
??我們以表格形式總結一下各個方法:
| 方法/處理方式 | 拋出異常 | 返回特殊值 | 一直阻塞 | 超時退出 |
|---|---|---|---|---|
| 插入方法 | add(E e) | offer(E e) | put(E e) | offer(E e, long timeout, TimeUnit unit) |
| 移除方法 | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
| 檢查方法 | element() | peek() | 不可用 | 不可用 |
??雖然看前文后已經大概了解各個列標題的含義,但是,這里再深入總結一把,請熟悉的猿友直接跳過:
-
拋出異常:當隊列滿時,如果再往隊列里插入元素,會拋出IllegalStateException(“Queuefull”)異常。當隊列空時,從隊列里獲取元素會拋出NoSuchElementException異常。
-
返回特殊值:當往隊列插入元素時,會返回元素是否插入成功,成功返回true。如果是移除方法,則是從隊列里取出一個元素,如果沒有則返回null。
-
一直阻塞:當阻塞隊列滿時,如果生產者線程往隊列里put元素,隊列會一直阻塞生產者線程,直到隊列可用或者響應中斷退出。 當隊列空時,如果消費者線程從隊列里take元素,隊列會阻塞住消費者線程,直到隊列不為空。
-
超時退出:當阻塞隊列滿時,如果生產者線程往隊列里插入元素,隊列會阻塞生產者線程一段時間,如果超過了指定的時間,生產者線程就會退出。
??如果是無界阻塞隊列,隊列不可能會出現被打滿的情況,所以使用put或offer方法永遠不會被阻塞,而且使用offer方法時,該方法永遠返回true。
ArrayBlockingQueue
??基于數組結構實現的有界阻塞隊列,按先進先出(FIFO)原則對任務排序。由于其容量固定,一旦創建,隊列的大小不能改變。在高并發場景下,若任務提交速度過快,可能會頻繁觸發拒絕策略,適用于對資源使用較為嚴格、任務量相對穩定的場景。創建隊列時需指定隊列大小,故需要仔細斟酌隊列長度,保證生產者和消費者速率相匹配。
實現原理
??內部維護一個數組用于存儲元素,通過ReentrantLock來保證線程安全。在進行插入和移除操作時,會獲取鎖,操作完成后釋放鎖。
核心輪子
??put(E e)方法用于將元素放入隊列,若隊列已滿則阻塞;take()方法用于從隊列中取出元素,若隊列為空則阻塞。下面提供一個調用add()和take() 方法的示例。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @Author Wiener
* @Date 2025-07-09
* @Description: 隊列超過指定容量
*/
public class FullQueueDemo {
public static void main(String[] args) {
// 初始化時指定隊列最大容量為3
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
try {
queue.add("測試1");
queue.add("測試2");
queue.add("測試3");
// 隊列已經飽和,不再接收新元素拋出異常
queue.add("我觸發異常");
} catch (IllegalStateException ie) {
System.out.println(ie);
try {
System.out.println("取出隊頭元素:" + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
// 輸出隊列中剩余元素個數
System.out.println("剩余元素個數:" + queue.size());
}
}
}
執行結果如下:
java.lang.IllegalStateException: Queue full
取出隊頭元素:測試1
剩余元素個數:2
LinkedBlockingQueue
??基于單向鏈表結構實現的阻塞隊列,按先進先出原則對任務排序。如果創建時沒有指定隊列容量,則默認是無界隊列,理論上大小為Integer.MAX_VALUE = 2^31 - 1;
??開發過程中使用無界隊列時,存在內存溢出風險,建議初始化的時候指定隊列長度。在并發場景下執行入隊和出隊操作時,ArrayBlockingQueue共用一把鎖,并發較低;而LinkedBlockingQueue分別使用了寫和讀兩把鎖,故吞吐量高于前者。
??適用場景不同,ArrayBlockingQueue適用于明確限制隊列大?。闯跏蓟笮『蟛荒軘U容)的場景,防止生產速度大于消費速度的時候,造成內存溢出、資源耗盡。LinkedBlockingQueue適用于業務高峰期可以自動擴容提升消費速度的場景。
??當使用該隊列時,線程池中的線程數量通常不會超過核心線程數,因為在核心線程都繁忙時,新來的任務就會被放入這個無界的隊列中等待執行,而不會創建新的線程(除非指定任務隊列容量且已滿時,還在提交任務)。這種隊列適用于任務量較大且執行時間較短的場景,可避免過多線程創建導致的資源耗盡問題,但需注意可能會因為任務堆積過多而耗盡內存。
實現原理
??使用單向鏈表存儲元素,同樣使用ReentrantLock保證線程安全,不過它有兩把鎖,分別用于入隊和出隊操作,減少鎖競爭。在隊列爆倉或為空時,通過Condition實現線程的阻塞和喚醒。
核心輪子
??put(E e)方法用于將元素放入隊列,take()方法用于從隊列中取出元素,與ArrayBlockingQueue類似,但由于理論上無界,put方法不會因為隊列滿而阻塞。
SynchronousQueue
??SynchronousQueue中文名是同步移交隊列,隊列長度為0,所以沒有容量。從名字就知道它的作用是一個線程向隊列插入數據的時候,必須一直阻塞等待另一個線程從隊列中取走數據。同樣,從隊列中取走數據的時候,必須等待另一個線程往隊列中插入數據。
基本特性
阻塞操作:put() 和 take() 操作會相互阻塞,直到一個插入操作和一個刪除操作配對成功為止。
線程安全:使用內部鎖ReentrantLock和條件變量Condition來確保線程安全。
高效傳輸:沒有內部容量用于存儲元素,適合一對一的直接數據交換,而不涉及數據的存儲。
應用場景
??由這些基本特性可知,SynchronousQueue更像是一種任務傳遞的媒介,適合于需要線程直接交換數據的場景,即把任務從生產者線程傳遞到消費者線程手上,保證了任務即時處理,不存在任務排隊等待的情況。SynchronousQueue 非常適合以下場景:
- 線程池:在 Executors.newCachedThreadPool() 方法中,SynchronousQueue 被用作線程池的工作隊列。它使得線程池能夠根據需要動態地擴展和收縮線程數量,弊端是可能導致線程池創建過多線程,這些線程都在競爭CPU時間片,等待CPU調度,最終會拖慢任務處理速度。
- 直接數據交換:在線程之間需要進行一對一數據交換的場景中使用SynchronousQueue,例如生產者和消費者之間直接交換數據而不需要中間存儲。
- 精細化調控任務執行速度:用于限制任務的執行速度,例如一個線程必須等待另一個線程完成后才能繼續處理任務。
實現原理
內部通過TransferQueue接口實現,采用一種復雜的 “配對” 機制。當一個線程執行插入操作(put)時,它會等待另一個線程來執行移除操作(take),兩者直接進行數據傳遞,而不經過隊列存儲。
??SynchronousQueue 使用 ReentrantLock 和 Condition 來實現線程間的等待和通知機制。每個 SynchronousQueue 實例都包含一個核心內部類 Transferer,該類使用了一種高效的基于鎖和條件變量的機制來管理生產者和消費者之間的直接通信,而無需存儲任何元素。這種設計使得 SynchronousQueue 在某些特定的高并發場景下非常高效。Transferer 有兩種基本模式:
直接傳輸:一個線程將元素傳遞給另一個線程,兩者直接進行數據交換。
請求等待:一個線程請求一個元素,如果沒有可用的元素,它將阻塞等待;另一個線程提供一個元素,然后喚醒等待的線程。
案例分析
??下面看一個SynchronousQueue的簡單用例。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
/**
* @Author Wiener
* @Date 2025-07-22
*/
public class SyncQueueTest {
// 創建一個 SynchronousQueue
private static BlockingQueue<Integer> queue = new SynchronousQueue<>();
public static void main(String[] args) {
// 生產者線程,往隊列中放5個元素
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " 插入數據: " + i);
// 阻塞插入
queue.put(i);
// 模擬延遲500毫秒
Thread.sleep(500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 消費者線程,從隊列中取出5個元素
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
Integer value = queue.take(); // 阻塞移除
System.out.println(Thread.currentThread().getName() + " 移除元素: " + value);
// 模擬延遲1000毫秒
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 啟動線程
producer.start();
consumer.start();
}
}
??輸出結果:
Thread-0 插入數據: 1
Thread-1 移除元素: 1
Thread-0 插入數據: 2
Thread-1 移除元素: 2
Thread-0 插入數據: 3
Thread-1 移除元素: 3
Thread-0 插入數據: 4
Thread-1 移除元素: 4
Thread-0 插入數據: 5
Thread-1 移除元素: 5
??在上述示例中,生產者線程使用函數 put() 將元素插入隊列,而消費者線程使用函數take()從隊列中取出元素。從輸出結果可以看到,生產者線程Thread-0往隊列放入一個元素后,就被阻塞了,直到消費者線程Thread-1從隊列中取走元素后,Thread-0才能繼續插入下一個元素。
LinkedBlockingDeque
??LinkedBlockingDeque 是 Java 并發包(java.util.concurrent)中的一個線性數據結構,它是一個基于鏈表實現的雙端阻塞隊列。這種隊列結合了阻塞隊列和雙端隊列(Deque)的特性,非常適合在多線程環境中使用,尤其是在生產者-消費者模式和工作竊取模式中。所謂雙端阻塞隊列指的是可以從隊列的兩端插入和移出元素。它因為多了一個操作隊列的入口,在多線程同時入隊時,也就減少了一半的競爭。
主要特性
??雙向隊列特性:支持在隊列的兩端進行插入和刪除操作,比如 addFirst()、addLast()、removeFirst() 和 removeLast()等。支持按照 FIFO(先進先出)或 FILO(后進先出)的方式處理數據。
??阻塞操作:如果隊列為空,嘗試獲取元素的操作會被阻塞,直到隊列中有元素可用。如果隊列已滿,嘗試添加元素的操作亦會被阻塞,直到隊列有空間可用。這些操作通常用于多線程場景,例如生產者-消費者模型。
??有界或無界隊列:默認情況下,LinkedBlockingDeque 的容量是無界的(默認大小為 Integer.MAX_VALUE)。但也可以在初始化時指定一個固定容量,使其成為有界隊列。
??線程安全:使用可重入鎖(ReentrantLock)和條件變量(Condition)來保證線程安全。通過鎖分離技術,提高了并發性能。
常用操作
插入操作:
- addFirst(E e):在隊列前端插入元素,如果隊列已滿則拋出異常。
- putFirst(E e):在隊列前端插入元素,如果隊列已滿則阻塞等待。
- offerFirst(E e, long timeout, TimeUnit unit):在隊列前端插入元素,如果隊列已滿則等待指定時間。
類似地,addLast()、putLast() 和 offerLast() 用于在隊列尾部插入元素。
刪除操作:
- removeFirst():移除并返回隊列的第一個元素,如果隊列為空則拋出異常。
- takeFirst():移除并返回隊列的第一個元素,如果隊列為空則阻塞等待。
- pollFirst(long timeout, TimeUnit unit):移除并返回隊列的第一個元素,如果隊列為空則等待指定時間。
類似地,removeLast()、takeLast() 和 pollLast() 用于從隊列尾部移除元素。
其它函數:
- size():返回當前隊列中精確的元素數量。
- remainingCapacity():返回隊列剩余的容量,如果是無界隊列,則始終返回 Integer.MAX_VALUE。
應用場景
-
生產者-消費者模式
多個線程可以并發地從隊列兩端進行插入或刪除操作,非常適合用于高效共享數據的生產者-消費者場景。 -
任務調度
作為任務隊列,管理線程池中的任務分發與執行。 -
緩存系統
用于臨時存儲和讀取數據,支持兩端快速插入與刪除。 -
雙向通信
由于支持雙端操作,適用于需要從兩端快速插入或刪除元素的場景。
??LinkedBlockingDeque示例代碼:
import java.util.concurrent.LinkedBlockingDeque;
public class LinkedBlockingDequeExample {
public static void main(String[] args) throws InterruptedException {
linkedBlockingDequeTest();
}
public static void linkedBlockingDequeTest() throws InterruptedException {
// 創建一個無界隊列
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
// 生產者線程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
System.out.println("隊列尾部添加元素: " + i);
deque.putLast(i); // 在隊列尾部添加元素
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 消費者線程
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
Integer value = deque.takeFirst(); // 從隊列頭部取出元素
System.out.println("隊列頭部消費元素: " + value);
Thread.sleep(150);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 啟動線程
producer.start();
consumer.start();
// 等待線程結束
producer.join();
consumer.join();
}
}
執行結果:
隊列尾部添加元素: 0
隊列頭部消費元素: 0
隊列尾部添加元素: 1
隊列頭部消費元素: 1
隊列尾部添加元素: 2
隊列尾部添加元素: 3
隊列頭部消費元素: 2
隊列尾部添加元素: 4
隊列頭部消費元素: 3
隊列尾部添加元素: 5
隊列頭部消費元素: 4
隊列尾部添加元素: 6
隊列尾部添加元素: 7
隊列頭部消費元素: 5
隊列尾部添加元素: 8
隊列頭部消費元素: 6
隊列尾部添加元素: 9
隊列頭部消費元素: 7
隊列頭部消費元素: 8
隊列頭部消費元素: 9
優點
??高效的并發性能:由于使用了鎖分離技術,讀寫操作可以并發執行,提高了性能。
??靈活的雙端操作:支持從隊列的兩端插入和刪除元素。
??阻塞特性:簡化了多線程編程,避免了忙等待。
缺點
??高并發下的性能問題:如果隊列大小設置不當,可能在高并發下導致資源爭用。
??內存占用:對于無界隊列,如果數據生產速度遠大于消費速度,可能導致內存溢出。
總結
??LinkedBlockingDeque 是一個非常強大的并發數據結構,結合了雙端隊列和阻塞隊列的優點,適用于需要高效并發處理任務的場景。它非常適合用于多線程環境下的任務調度、數據緩存、生產者-消費者模型等。如果你有更多關于 LinkedBlockingDeque 的具體問題或需要進一步的示例,請在評論區告訴我!希望這些信息對你有幫助! ??
PriorityBlockingQueue
??一個支持優先級排序的無界阻塞隊列,任務必須實現Comparable接口,或者在創建隊列時提供Comparator。它會根據任務的優先級對任務進行排序,優先級高的任務優先執行;適用于有任務優先級區分的場景,確保重要任務能優先得到處理。
基本特性
-
無界隊列:PriorityBlockingQueue 是一個基于數組的無界隊列,其默認初始容量為 11,并可以根據需要動態擴展,最大容量為 Integer.MAX_VALUE - 8。
-
優先級排序:隊列中的元素按照優先級進行排序。默認情況下,元素按照自然順序排列(例如數字從小到大),也可以通過自定義 Comparator 來定義優先級規則。
-
線程安全:它是一個線程安全的隊列,適用于多線程環境。
-
阻塞操作:當隊列為空時,從隊列中獲取元素的操作(如 take())會阻塞,直到隊列中有元素可用。
應用場景
- 任務調度:適用于需要根據優先級處理任務的場景,例如任務隊列中高優先級任務需要優先處理。
- 資源管理:在多線程環境中,管理需要優先級排序的資源分配。
- 事件處理:處理需要按照優先級順序執行的事件流。
??例如,在一個監控系統中,告警任務有不同的級別,級別高的告警任務(如服務器宕機告警)優先級更高,需要優先處理,就可以將這些告警任務放入PriorityBlockingQueue中,按照優先級高低依次執行。
實現原理
- 基于堆結構:PriorityBlockingQueue 內部使用堆(heap)數據結構來維護元素的優先級。堆是一種完全二叉樹結構,能夠保證每次出隊的元素是優先級最高(或最低)的元素。
- ReentrantLock:為了保證線程安全,隊列內部使用了 ReentrantLock 鎖機制,確保多線程環境下操作的安全性。
注意事項
- 非公平鎖:PriorityBlockingQueue 默認使用非公平鎖,這意味著在競爭激烈的情況下,某些線程可能會“饑餓”。
- 不支持 null 元素:隊列中不允許插入 null 元素。
- 性能開銷:由于每次插入或刪除元素都需要調整堆結構,因此性能開銷相對較大。
PriorityBlockingQueue 是一個非常強大的并發工具,適合需要處理優先級任務的場景。它的無界特性、線程安全和阻塞機制使其在多線程編程中非常實用。如果你有更具體的PriorityBlockingQueue問題或需要其它相關示例,請隨時在評論區告訴我!??
DelayQueue
??DelayQueue是本地延時隊列,一種實現了延遲功能的無界阻塞隊列,基于PriorityQueue實現。譬如希望任務在5秒后執行,就可以使用DelayQueue實現,有如下常見的使用場景:
- 火車票訂單20分鐘內未支付,就取消
- 刪除緩存中過期元素
- 延遲發送業務消息
??DelayQueue底層采用組合的方式,復用PriorityQueue的、按照延遲時間排序任務的功能,實現了延遲隊列。它是線程安全的,內部使用ReentrantLock加鎖。關于示例,各位可以移步《轉:基于Redis實現延時隊列》3.2節【DelayQueue 延時隊列】。
阻塞隊列的光環
業務無關
??業務無關,一個具有普適性質的消息隊列組件,不需要考慮上層的業務模型,只做好消息的分發就可以了,上層業務的不同模塊反而需要依賴消息隊列所定義的規范進行通信。
容災
??對于普適的消息隊列組件來說,節點的動態增刪和消息的持久化都是支持其容災能力的重要基本特性。關于吞吐量就不必多說了,消息隊列的吞吐量上去了,整個系統的內部通信效率自然會得到顯著提高。
削峰填谷
??這個就是很典型的一個MQ的用法,用有限的機器資源承載高并發請求。如果業務場景允許異步削峰,高峰期積壓一些請求在MQ里,然后高峰期過了,后臺系統在一定時間內消費完畢且隊列不再積壓消息的話,那就很適合選擇這種阻塞隊列技術架構方案。
阻塞隊列的阿喀琉斯之踵
??我們從上一節得知阻塞隊列居然有那么多閃閃發光的優點,請問,它有缺點嗎?答案是“有”,這里就羅列一些。
??阻塞隊列的阿喀琉斯之踵主要包括內存溢出和資源耗盡,茲從以下幾個方面展開介紹:
內存消耗
??由于無界阻塞隊列理論上可以無限增長,系統設計者難以對資源使用量進行精確規劃和限制,如果生產者持續快速地向隊列中添加元素而消費者無法及時處理,可能會導致大量數據堆積在內存中,最終可能造成內存溢出(拋出OutOfMemoryError)、資源耗盡。
性能問題
??當隊列中的元素數量非常龐大時,某些操作(如插入或刪除)可能會變得效率低下。例如,在基于優先級的無界阻塞隊列中,維護元素順序的操作成本會隨著隊列規模增大而增加。
延遲與公平性
??以DelayQueue為例,它只有在延遲到期后才能被消費。如果存在大量未到期的任務,新加入且即將到期的任務可能需要等待較長時間才能被執行,影響了任務執行的及時性和公平性。
一致性問題
??A系統處理完請求直接返回成功標識了,用戶以為這個請求就成功了;但是問題是,對于被調用的B、C和D系統,如果B和D兩個系統寫庫成功了,結果C系統寫庫失敗了,這數據就不一致了。
拔高系統復雜度
??硬生生塞進個MQ系統進來后,怎么做到消息沒有重復消費?怎么處理消息丟失的場景?怎么保證消息傳遞的順序性?為了應對這些問題,開發者往往需要引入額外機制來監控和管理隊列狀態,從而增加了系統的復雜度。
??結束語
??Java 提供的這幾種 BlockingQueue 的實現,每種實現適用于不同的使用場景。選擇合適的 BlockingQueue 實現類是構建高效并發程序的關鍵之一。以下是常見的 BlockingQueue 實現及其適用場景,幫助你做出最佳選擇:
| 實現類 | 類型 | 是否有界 | 是否阻塞 | 特點 |
|---|---|---|---|---|
| ArrayBlockingQueue | 有界隊列 | 有界 | 阻塞 | 基于數組,線程安全,性能穩定 |
| LinkedBlockingQueue | 無界/有界 | 默認無界 | 阻塞 | 強一致性,吞吐量高,適合生產者速率高于消費者的場景 |
| PriorityBlockingQueue | 無界優先隊列 | 無界 | 阻塞 | 元素按優先級排序 |
| SynchronousQueue | 同步移交隊列 | 容量為0 | 阻塞 | 不存儲元素,直接在生產者和消費者之間傳遞 |
| DelayQueue | 延遲隊列 | 無界 | 阻塞 | 元素只有在延遲期滿后才能取出 |
| LinkedBlockingDeque | 雙端阻塞隊列 | 默認無界 | 阻塞 | 強一致性,支持從兩端插入和移除元素 |
??不同的阻塞隊列有著各自的特點和適用場景,在實際使用線程池時,需要根據具體的業務需求、系統資源狀況以及對任務執行順序、響應時間等方面的要求,合理選擇相應的隊列來構建線程池,以實現高效的任務處理。如果你還有其它具體需求或疑問,請隨時在評論區留言!希望你今天過得愉快~
Reference
Buy me a coffee. ?Get red packets.
浙公網安備 33010602011771號