写在前面
An unbounded {@linkpain java.util.concurrent.BlockingQueue blocking queue} that uses the same ordering rules as class {@link PriorityQueue} and supplies blocking retrieval operations.
源码分析自 JDK 1.8.0_171
PriorityBlockingQueue,无界优先级阻塞队列。队列中的优先级根据提供的独立的一个 Comparator
接口或者实现 Comparable
接口的队列元素决定。
概述
优先级队列是基于堆(小顶堆)是实现的。线程安全性由内部声明的一个ReentrantLock保证,即所有的公共操作都是在锁下完成。
堆
堆实际上是一种完全二叉树,分为大顶堆和小顶堆。大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。如下图:
一般堆都是由数组实现,记一个节点的索引下标为n,那么它的左右孩子为 2n+1
和 2(n+1)
。
内部变量
- private static final int DEFAULT_INITIAL_CAPACITY = 11; // 默认数组大小
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 数组支持最大大小
- private transient Object[] queue; // 存储元素底层数组,以堆的形式
- private transient int size; // 队列中元素数量
- private transient Comparator<? super E> comparator; // 用于区分优先级的比较接口
- private final ReentrantLock lock; // 保证线程安全的锁
- private final Condition notEmpty; // lock.condition
- private transient volatile int allocationSpinLock; // 用于数组扩展时的自旋锁
构造方法
- public PriorityBlockingQueue();
1 | /** |
队列默认大小为11
**
- public PriorityBlockingQueue(int initialCapacity);
1 | /** |
未传入comparator,则需要队列元素自身实现ComParable接口
- public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator);
1 | /** |
- public PriorityBlockingQueue(Collection<? extends E> c);
1 | /** |
有必要的话,需要堆化
源码分析
PriorityBlockingQueue的最主要的两个动作时,往队列中放入元素,从队列中取出元素。对应的两个核心的方法时 offer
和 poll
。此外还有在上述其中一个构造方法中,设计到一个堆化的操作,对应的方法时 heapify
。
heapify
以堆的形式重新组织数组中的元素。
1 | /** |
offer
队列是无界的,所以理论上offer永远返回true。
1 | /** |
扩容的时候有点讲究,见下
1 | /** |
poll
需要在持有锁的前提下,才能从队列中获取元素。获取元素成功后,原则上堆结构是被_破坏_了,所以需要重新调整堆结构。
1 | public E poll() { |
调整堆
上文提到的调整堆的操作有 siftDownComparable(siftDownUsingComparator)
和 siftUpComparable(siftUpUsingComparator)
。siftDownComparable
和 siftDownUsingComparator
是同一种操作,只是比较元素大小一个是利用元素实现 Comparable
接口,一个是利用构造时传入的 Comparator
。
siftDownComparable
1 | /** |
siftUpComparable
1 | /** |
总结
优先级队列内部数据结构是一个数组,这个数组是以堆的形式组织的。线程安全性由内部的一个 ReentrantLock
保证,所有对元素的公共操作是需要在持有锁的前提下才能完成。
出入队后,我们说这个堆结构是被破坏了,所以需要重新调整下这个堆结构。调整堆主要是由两个操作组成:siftUp[Comparable|Comparator]
和 siftDown[Comparable|Comparator]
。分别是从下往上找替换的元素位置,从上往下找替换元素的位置。