Java中PriorityBlockingQueue的使用
一、核心特性與設(shè)計(jì)目標(biāo)
PriorityBlockingQueue 是 Java 并發(fā)包(java.util.concurrent)中基于 優(yōu)先級(jí)堆 實(shí)現(xiàn)的無(wú)界阻塞隊(duì)列,核心特性如下:
- 無(wú)界隊(duì)列:默認(rèn)容量為 Integer.MAX_VALUE,僅受內(nèi)存限制。
- 優(yōu)先級(jí)排序:元素按自然順序(實(shí)現(xiàn) Comparable)或自定義 Comparator 排序,優(yōu)先級(jí)最高者先出隊(duì)。
- 線程安全:通過(guò) ReentrantLock 和 Condition 實(shí)現(xiàn)并發(fā)控制。
- 阻塞特性:隊(duì)列為空時(shí),take() 阻塞;插入操作永不阻塞(無(wú)界特性)。
- 弱一致性迭代器:遍歷時(shí)可能看到部分更新,但不會(huì)拋出 ConcurrentModificationException。
二、內(nèi)部數(shù)據(jù)結(jié)構(gòu)與實(shí)現(xiàn)原理
1. 底層存儲(chǔ)結(jié)構(gòu)
數(shù)組實(shí)現(xiàn)的二叉堆:默認(rèn)最小堆(堆頂為最小元素),通過(guò)索引關(guān)系維護(hù)父子節(jié)點(diǎn):
// 父節(jié)點(diǎn)索引 parent(i) = (i-1) >>> 1 // 左子節(jié)點(diǎn)索引 leftChild(i) = 2*i + 1 // 右子節(jié)點(diǎn)索引 rightChild(i) = 2*i + 2
示例:數(shù)組 `` 對(duì)應(yīng)的堆結(jié)構(gòu)如上圖所示。
2. 核心字段
private transient Object[] queue; // 存儲(chǔ)元素的數(shù)組 private transient int size; // 當(dāng)前元素?cái)?shù)量 private final ReentrantLock lock; // 主鎖 private final Condition notEmpty; // 隊(duì)列非空條件 private transient Comparator<? super E> comparator; // 比較器
3. 擴(kuò)容機(jī)制
- 動(dòng)態(tài)擴(kuò)容:當(dāng)數(shù)組滿時(shí),按以下規(guī)則擴(kuò)容:
- 小容量(<64):容量翻倍 + 2
- 大容量:容量增長(zhǎng) 50%
- 最大容量:
Integer.MAX_VALUE - 8(防止內(nèi)存溢出)。
- 無(wú)鎖擴(kuò)容:通過(guò) CAS 操作(
allocationSpinLock)控制并發(fā)擴(kuò)容,避免線程阻塞。
三、核心方法與操作流程
1. 插入操作(offer())
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
lock.lock();
try {
// 檢查是否需要擴(kuò)容
if (size >= queue.length) grow();
// 上浮操作維護(hù)堆性質(zhì)
siftUp(size, e);
size++;
notEmpty.signal(); // 喚醒等待的消費(fèi)者
return true;
} finally {
lock.unlock();
}
}
- 關(guān)鍵步驟:擴(kuò)容檢查 → 上?。?code>siftUp)調(diào)整堆結(jié)構(gòu) → 喚醒消費(fèi)者線程。
2. 出隊(duì)操作(take())
public E take() throws InterruptedException {
lock.lockInterruptibly();
try {
while (size == 0) notEmpty.await(); // 隊(duì)列空時(shí)阻塞
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
E result = (E) queue; // 取出堆頂元素
E x = (E) queue@ref; // 最后一個(gè)元素移到堆頂
queue= null; // 清除原堆頂
if (size > 0) siftDown(0, x); // 下沉操作維護(hù)堆性質(zhì)
return result;
}
- 關(guān)鍵步驟:阻塞等待 → 取出堆頂 → 下沉(
siftDown)調(diào)整堆結(jié)構(gòu)。
3. 堆調(diào)整操作
- 上?。╯iftUp):新元素插入后,與其父節(jié)點(diǎn)比較,若優(yōu)先級(jí)更高則交換,直到滿足堆性質(zhì)。
- 下沉(siftDown):堆頂元素與子節(jié)點(diǎn)比較,若優(yōu)先級(jí)較低則交換,直到滿足堆性質(zhì)。
四、線程安全與性能優(yōu)化
1. 鎖機(jī)制
- 單鎖設(shè)計(jì):所有修改操作(插入/刪除)共享同一把
ReentrantLock,簡(jiǎn)化實(shí)現(xiàn)但可能成為高并發(fā)瓶頸。 - 條件變量:僅
notEmpty用于消費(fèi)者等待,無(wú)notFull(因無(wú)界)。
2. 性能指標(biāo)
- 插入時(shí)間復(fù)雜度:O(log n)(上浮操作)
- 刪除時(shí)間復(fù)雜度:O(log n)(下沉操作)
- 吞吐量:約 100,000-200,000 Ops/ms(8線程),低于
ConcurrentLinkedQueue。
五、適用場(chǎng)景與代碼示例
1. 典型場(chǎng)景
- 任務(wù)調(diào)度系統(tǒng):高優(yōu)先級(jí)任務(wù)優(yōu)先執(zhí)行(如緊急訂單處理)。
- 事件驅(qū)動(dòng)架構(gòu):按事件緊急程度處理(如實(shí)時(shí)監(jiān)控告警)。
- 資源分配:VIP用戶優(yōu)先獲取資源(如數(shù)據(jù)庫(kù)連接池)。
2. 代碼示例
// 自定義任務(wù)類(降序優(yōu)先級(jí))
class Task implements Comparable<Task> {
private int priority;
public Task(int priority) { this.priority = priority; }
@Override
public int compareTo(Task o) {
return Integer.compare(o.priority, this.priority); // 降序排列
}
}
// 使用示例
PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
queue.put(new Task(3)); // 插入低優(yōu)先級(jí)任務(wù)
queue.put(new Task(1)); // 插入高優(yōu)先級(jí)任務(wù)
Task task = queue.take(); // 取出優(yōu)先級(jí)1的任務(wù)
六、與其他隊(duì)列的對(duì)比
| 特性 | PriorityBlockingQueue | ArrayBlockingQueue | ConcurrentLinkedQueue |
|---|---|---|---|
| 容量 | 無(wú)界 | 有界 | 無(wú)界 |
| 排序 | 支持優(yōu)先級(jí) | FIFO | 無(wú)序 |
| 鎖機(jī)制 | 單鎖 | 單鎖 | 無(wú)鎖 |
| 適用場(chǎng)景 | 優(yōu)先級(jí)調(diào)度 | 有界緩沖 | 高并發(fā)無(wú)序隊(duì)列 |
七、優(yōu)缺點(diǎn)總結(jié)
優(yōu)點(diǎn)
- 自動(dòng)排序:無(wú)需手動(dòng)管理優(yōu)先級(jí),簡(jiǎn)化代碼邏輯。
- 線程安全:內(nèi)置鎖機(jī)制保障并發(fā)安全。
- 無(wú)界設(shè)計(jì):避免生產(chǎn)者線程因隊(duì)列滿而阻塞。
缺點(diǎn)
- 內(nèi)存風(fēng)險(xiǎn):無(wú)界可能導(dǎo)致內(nèi)存溢出,需監(jiān)控隊(duì)列大小。
- 單鎖瓶頸:高并發(fā)下性能受限,可考慮分片隊(duì)列優(yōu)化。
- 不支持延遲:需結(jié)合
ScheduledThreadPoolExecutor實(shí)現(xiàn)延遲任務(wù)。
八、源碼設(shè)計(jì)亮點(diǎn)
- 堆化操作:通過(guò)
heapify()方法將普通數(shù)組快速轉(zhuǎn)換為堆結(jié)構(gòu)。 - 自旋鎖擴(kuò)容:使用
allocationSpinLock減少擴(kuò)容時(shí)的線程阻塞。 - 弱一致性迭代:迭代器遍歷時(shí)允許并發(fā)修改,避免
ConcurrentModificationException。
九、最佳實(shí)踐
- 合理設(shè)置初始容量:根據(jù)預(yù)估數(shù)據(jù)量減少擴(kuò)容次數(shù)。
- 自定義比較器:明確優(yōu)先級(jí)規(guī)則,避免自然排序歧義。
- 監(jiān)控隊(duì)列狀態(tài):通過(guò)
size()和remainingCapacity()預(yù)防內(nèi)存溢出。 - 結(jié)合線程池使用:與
ThreadPoolExecutor集成實(shí)現(xiàn)優(yōu)先級(jí)任務(wù)調(diào)度。
通過(guò)合理利用 PriorityBlockingQueue,可高效實(shí)現(xiàn)基于優(yōu)先級(jí)的并發(fā)任務(wù)處理,但需注意其無(wú)界特性帶來(lái)的內(nèi)存風(fēng)險(xiǎn)。在需要嚴(yán)格容量控制的場(chǎng)景中,可考慮使用 ArrayBlockingQueue 或分片策略。
到此這篇關(guān)于Java中PriorityBlockingQueue的使用的文章就介紹到這了,更多相關(guān)Java PriorityBlockingQueue內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java并發(fā)包中的PriorityBlockingQueue深度解析
- Java線程池隊(duì)列PriorityBlockingQueue原理分析
- Java的PriorityBlockingQueue優(yōu)先級(jí)阻塞隊(duì)列代碼實(shí)例
- 詳解Java并發(fā)編程中的優(yōu)先級(jí)隊(duì)列PriorityBlockingQueue
- Java線程池隊(duì)列PriorityBlockingQueue和SynchronousQueue詳解
- java并發(fā)編程工具類PriorityBlockingQueue優(yōu)先級(jí)隊(duì)列
- java中PriorityBlockingQueue的入隊(duì)知識(shí)點(diǎn)總結(jié)
相關(guān)文章
Retrofit+RxJava實(shí)現(xiàn)帶進(jìn)度下載文件
這篇文章主要為大家詳細(xì)介紹了Retrofit+RxJava實(shí)現(xiàn)帶進(jìn)度下載文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Java中Comparator與Comparable排序的區(qū)別詳解
這篇文章主要介紹了Java中Comparator與Comparable排序的區(qū)別詳解,如果你有一個(gè)類,希望支持同類型的自定義比較策略,可以實(shí)現(xiàn)接口Comparable,如果某個(gè)類,沒有實(shí)現(xiàn)Comparable,但是又希望對(duì)它進(jìn)行比較,則可以自定義一個(gè)Comparator,需要的朋友可以參考下2024-01-01
Java?超詳細(xì)講解類的定義方式和對(duì)象的實(shí)例化
Java是一門純面向?qū)ο蟮恼Z(yǔ)言(Object?Oriented?Program,繼承OOP),在面對(duì)對(duì)象的世界里面,一切皆為對(duì)象。面向?qū)ο笫墙鉀Q問題的一種思想,主要依靠對(duì)象之間的交互完成一件事情2022-03-03

