PriorityBlockingQueue是优先级阻塞队列,实现跟PriorityQueue基本一模一样
1 PriorityQueue
非阻塞的优先级队列,底层基于数组实现,可以扩容,是真正意义上的无界队列,优先级基于二叉堆实现
1.1 二叉堆:
- 二叉堆是一颗完整的二叉树
- 任意一个节点大于父节点 或者 小于父节点
1.2 构造器
- 构造器内可以穿入比较器,通过比较器来决定是大顶堆还是小顶堆
// 默认小顶堆
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
// 数组的初始长度
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
// 比较器,决定大顶堆还是小顶堆
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
// 实际的初始化构造器
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
- 也可以通过传入Collection和PriorityQueue来初始化,如果Collection是SortedSet,会继续使用内部的比较器
public PriorityQueue(Collection<? extends E> c) {
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
// 使用SortedSet的比较器
this.comparator = (Comparator<? super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue<?>) {
PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
// 使用PriorityQueue的比较器
this.comparator = (Comparator<? super E>) pq.comparator();
initFromPriorityQueue(pq);
}
else {
// 其他情况默认null
this.comparator = null;
initFromCollection(c);
}
}
public PriorityQueue(PriorityQueue<? extends E> c) {
this.comparator = (Comparator<? super E>) c.comparator();
initFromPriorityQueue(c);
}
public PriorityQueue(SortedSet<? extends E> c) {
this.comparator = (Comparator<? super E>) c.comparator();
initElementsFromCollection(c);
}
1.3 存取数据(保证二叉堆结构)
1.3.1 存数据保证二叉堆结构
// 优先级队列添加操作,确定如何保证小顶堆结构
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
// size是数组数据条数,大于等于数组长度后,需要扩容
int i = size;
if (i >= queue.length)
// Double size if small; else grow by 50%
grow(i + 1);
// size + i,数据多一条
size = i + 1;
// 如果i == 0,说明添加的是第一个数据
if (i == 0)
queue[0] = e;
else
// 不是第一个数据,Up上移保证结构
siftUp(i, e);
return true;
}
// 让当前节点和父节点比较,如果当前节点比较小,就上移
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}
1.3.2 取数据保证二叉堆结构
// 取堆顶数据
public E poll() {
// 没有数据返回null
if (size == 0)
return null;
// 最后一个数据的索引
int s = --size;
// 需要全都的数据
E result = (E) queue[0];
// 取出最后一个数据
E x = (E) queue[s];
// 将最后一个数据置位null
queue[s] = null;
if (s != 0)
// 下移保证安全
siftDown(0, x);
return result;
}
// 堆顶数据下移,知道last数据可以存放的位置,然后替换即可
private void siftDownUsingComparator(int k, E x) {
while (k < half) {
int child = (k << 1) + 1;
// 找到左子
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
2 PriorityBlockingQueue
PriorityBlockingQueue底层基于数组,可以扩容,实现跟PriorityQueue基本一模一样,只是PriorityBlockingQueue基于Lock锁实现了多线程操作安全
2.1 构造器
构造器基本与PriorityQueue相同,仅有添加Collection的方法略有不同
public PriorityBlockingQueue(Collection<? extends E> c) {
// 不能排序时为true
boolean heapify = true; // true if not known to be in heap order
// null值筛选
boolean screen = true; // true if must screen for nulls
if (c instanceof SortedSet<?>) {
SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
this.comparator = (Comparator<? super E>) ss.comparator();
// 已有比较器
heapify = false;
}
else if (c instanceof PriorityBlockingQueue<?>) {
PriorityBlockingQueue<? extends E> pq =
(PriorityBlockingQueue<? extends E>) c;
this.comparator = (Comparator<? super E>) pq.comparator();
// 从PriorityBlockingQueue获取数据初始化,不需要考虑null
screen = false;
if (pq.getClass() == PriorityBlockingQueue.class) // exact match
// 已有比较器
heapify = false;
}
Object[] es = c.toArray();
int n = es.length;
if (c.getClass() != java.util.ArrayList.class)
es = Arrays.copyOf(es, n, Object[].class);
if (screen && (n == 1 || this.comparator != null)) {
// 筛选null值
for (Object e : es)
if (e == null)
throw new NullPointerException();
}
this.queue = ensureNonEmpty(es);
this.size = n;
if (heapify)
// 排序处理
heapify();
}
2.2 PriorityBlockingQueue存取数据源码分析
2.2.1 存数据
存数据不需要挂起线程,所有的存数据最终都是调用offer方法
- offer方法
// 所有添加都走这里,没有await挂起的方式,
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
int n, cap;
Object[] array;
// 扩容,允许多线程并发扩容。见后文
while ((n = size) >= (cap = (array = queue).length))
tryGrow(array, cap);
try {
Comparator<? super E> cmp = comparator;
if (cmp == null)
//添加数据到二叉堆
siftUpComparable(n, e, array);
else
siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
// 唤醒读线程
notEmpty.signal();
} finally {
lock.unlock();
}
return true;
}
// 跟PriorityQueue一样的上移操作
private static <T> void siftUpComparable(int k, T x, Object[] array) {
Comparable<? super T> key = (Comparable<? super T>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = array[parent];
if (key.compareTo((T) e) >= 0)
break;
array[k] = e;
k = parent;
}
array[k] = key;
}
- put和offer(E e, long timeout, TimeUnit unit)都是直接调用offer()
public void put(E e) {
offer(e); // never need to block
}
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e); // never need to block
}
- 扩容操作:允许多线程并发扩容的。(不是协助扩容),但是只有一个线程会成功,基于CAS的方式,避免并发问题
private void tryGrow(Object[] array, int oldCap) {
lock.unlock();
Object[] newArray = null;
// 线程将allocationSpinLock从0改为1(CAS),得到了扩容的权利,可以创建新数组
if (allocationSpinLock == 0 && UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,0, 1)) {
try {
// 计算新数组长度
int newCap = oldCap + ((oldCap < 64) ? (oldCap + 2) : (oldCap >> 1));
// 判断长度是否超过界限
if (newCap - MAX_ARRAY_SIZE > 0) {
int minCap = oldCap + 1;
if (minCap < 0 || minCap > MAX_ARRAY_SIZE)
throw new OutOfMemoryError();
newCap = MAX_ARRAY_SIZE;
}
if (newCap > oldCap && queue == array)
// 创建新数组
newArray = new Object[newCap];
} finally {
allocationSpinLock = 0;
}
}
if (newArray == null)
// 如果newArray是null,说明当前线程没有执行扩容操作
// 让出CPU时间片,尽量让扩容的线程先走完扩容操作
Thread.yield();
lock.lock();
if (newArray != null && queue == array) {
queue = newArray;
// 扩容结束
System.arraycopy(array, 0, newArray, 0, oldCap);
}
}
2.2.2 取数据
PriorityBlockingQueue的读操作,是允许使用condition挂起的,因为二叉堆可能没有数据。
- dequeue,实际的取数据并处理堆结构的方法,队列为空时会返回null
private E dequeue() {
int n = size - 1;
if (n < 0)
return null;
else {
Object[] array = queue;
// 拿到堆顶数据
E result = (E) array[0];
E x = (E) array[n];
array[n] = null;
Comparator<? super E> cmp = comparator;
if (cmp == null)
// 保证结构,下移
siftDownComparable(0, x, array, n);
else
siftDownUsingComparator(0, x, array, n, cmp);
size = n;
return result;
}
}
- poll,取数据,加锁调用dequeue,队列为空返回null
public E poll() {
// 基于lock锁保证安全,
final ReentrantLock lock = this.lock;
lock.lock();
try {
return dequeue();
} finally {
lock.unlock();
}
}
- poll,取数据,队列为空时超时等待,超时后返回null
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null && nanos > 0)
nanos = notEmpty.awaitNanos(nanos);
} finally {
lock.unlock();
}
return result;
}
- take,取数据,队列为空时阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
// 队列为空时阻塞
while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}