PriorityBlockingQueue 是基于最小堆实现的线程安全无界优先队列,使用 ReentrantLock 保证操作原子性,take() 阻塞等待非空,不保证相同优先级 FIFO,不支持 null 和动态优先级更新。

PriorityBlockingQueue 是 Java 并发包(java.util.concurrent)中一个线程安全的无界优先队列,它内部使用可重入的堆结构(最小堆)实现元素排序,并在出队操作(take())时自动阻塞,直到队列非空。它不保证相同优先级元素的处理顺序(即不具有 FIFO 稳定性),也不支持 null 元素。
基于最小堆的优先级排序机制
PriorityBlockingQueue 底层维护一个动态数组模拟的最小堆:队首(索引 0)始终是当前优先级最高(即“最小”)的元素。插入(offer())和删除(poll()/take())都通过上浮(siftUp)或下沉(siftDown)调整堆结构,时间复杂度均为 O(log n)。元素比较依赖其自然顺序(实现 Comparable)或构造时传入的 Comparator。
- 堆不是完全有序数组,仅满足父子节点的偏序关系(父 ≤ 左右子)
- 遍历迭代器返回的顺序不保证优先级顺序,仅用于遍历,不能用于排序结果消费
- 若自定义 Comparator 返回 0,不代表两个对象逻辑相等,堆仍可能将其视为不同节点处理
无锁 + ReentrantLock 实现的线程安全
不同于基于 CAS 的无锁队列,PriorityBlockingQueue 使用单个 ReentrantLock 保护所有结构性修改(如 offer、poll、take)。这意味着:
- 同一时刻只允许一个线程执行入队或出队操作,避免并发修改导致堆结构错乱
- take() 在队列为空时会释放锁并进入
Condition等待,被offer()成功后唤醒,实现阻塞语义 - 没有读写分离,size()、isEmpty() 等方法也需加锁,轻量但非无开销
阻塞逻辑的关键行为细节
阻塞仅发生在 take() 方法——它会一直等待,直到队列中至少有一个元素;而 poll(long, TimeUnit) 支持超时等待。poll()(无参)则立即返回 null(非阻塞)。注意:
- 即使有高优先级元素入队,正在阻塞的 take() 也会被唤醒并重新竞争锁,确保“及时响应”
- 多个线程调用 take() 时,它们共享同一个 condition,唤醒顺序取决于 JVM 线程调度,不保证公平性
- 中断正在 take() 的线程会抛出
InterruptedException,并清除中断状态
典型适用场景与注意事项
适合用于任务调度器、带优先级的消息分发、资源抢占式请求队列等场景,但需注意:
- 不支持扩容失败回退,虽称“无界”,但实际受堆内存限制,OOM 风险存在
- 无法按条件删除中间元素(如 removeIf),仅支持基于对象引用的
remove(Object),且触发 O(n) 遍历+O(log n) 堆调整 - 若元素的优先级在入队后发生改变(且未重新入队),堆结构将失效,引发不可预期排序










