基于优先堆的无界阻塞队列,PriorityQueue的线程安全版本。
基于数组的平衡二叉堆,在PriorityQueue基础上,增加了一把锁、一个条件:
private transient Object[] queue;
// 增删查公用的锁
private final ReentrantLock lock;
// 队列为空时,阻塞take/poll线程的条件
private final Condition notEmpty;
// 自旋锁,用CAS方式获取,用于动态扩展queue
private transient volatile int allocationSpinLock;
构造器
与PriorityQueue几乎一样,除了需要对lock、notEmpty初始化:
public PriorityBlockingQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
public PriorityBlockingQueue(int initialCapacity) {
this(initialCapacity, null);
}
public PriorityBlockingQueue(int initialCapacity,
Comparator super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.lock = new ReentrantLock(); // lock、notEmpty初始化
this.notEmpty = lock.newCondition();
this.comparator = comparator;
this.queue = new Object[initialCapacity];
}
public PriorityBlockingQueue(Collection extends E> c) {
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
boolean heapify = true; // true if not known to be in heap order
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();
screen = false;
if (pq.getClass() == PriorityBlockingQueue.class) // exact match
heapify = false;
}
Object[] a = c.toArray();
int n = a.length;
// If c.toArray incorrectly doesn't return Object[], copy it.
if (a.getClass() != Object[].class)
a = Arrays.copyOf(a, n, Object[].class);
if (screen && (n == 1 || this.comparator != null)) {
for (int i = 0; i < n; ++i)
if (a[i] == null)
throw new NullPointerException();
}
this.queue = a;
this.size = n;
if (heapify)
heapify();
}
步骤(与PriorityQueue大致相同,除了采用自旋锁的方式动态分配数组,在获取公用锁下复制queue):
1)当queue已满,若有元素入队请求,则进行容量扩展;
2)在获取公用锁lock的前提下,释放lock,采用自旋锁的方式动态扩展数组,允许与take/poll线程并发,完成分配后再重新获取lock;
3)oldCap小于64则容量翻倍;否则增长50%;
4)检查newCap是否在MAX_ARRAY_SIZE范围内,若minCap有overflow或大于MAX_ARRAY_SIZE,抛出OutOfMemoryError异常;否则容量最大不超过MAX_ARRAY_SIZE;
5)动态分配新容量的Object[];
6)获取公用锁,将旧queue中的元素复制过来。
while ((n = size) >= (cap = (array = queue).length)) // while使用是为了采用自旋锁进行扩展queue
tryGrow(array, cap);
// 动态扩展queue
// 释放公用锁,采用自旋锁,允许扩展过程中与take/poll线程并发,避免其在该过程中的等待
private void tryGrow(Object[] array, int oldCap) {
lock.unlock(); // 释放公用锁
Object[] newArray = null;
if (allocationSpinLock == 0 && // 采用CAS方式获取allocationSpinLock,进行动态分配
UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,
0, 1)) {
try {
int newCap = oldCap + ((oldCap < 64) ?
(oldCap + 2) : // grow faster if small
(oldCap >> 1));
if (newCap - MAX_ARRAY_SIZE > 0) { // possible overflow
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) // 其他线程正进行扩展,当前线程yield
Thread.yield();
lock.lock(); // 重新获取公用锁
if (newArray != null && queue == array) { // 复制队列
queue = newArray;
System.arraycopy(array, 0, newArray, 0, oldCap);
}
}
与PriorityQueue一样,除了增加两个参数:Object[] array、Comparator super T> cmp,以保证并发性:
// 对元素x,从k往前移,保持二叉堆的平衡性
private static void siftUpUsingComparator(int k, T x, Object[] array,
Comparator super T> cmp) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = array[parent];
if (cmp.compare(x, (T) e) >= 0)
break;
array[k] = e;
k = parent;
}
array[k] = x;
}
// 对元素x,从k往后移,保持二叉堆的平衡性
private static void siftDownUsingComparator(int k, T x, Object[] array,
int n,
Comparator super T> cmp) {
if (n > 0) {
int half = n >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = array[child];
int right = child + 1;
if (right < n && cmp.compare((T) c, (T) array[right]) > 0)
c = array[child = right];
if (cmp.compare(x, (T) c) <= 0)
break;
array[k] = c;
k = child;
}
array[k] = x;
}
}
步骤(过程与PriorityQueue一样,除了考虑并发性加锁):
1)获取公用锁lock;
2)检查队列是否已满,若满则采用自旋锁的方式进行容量扩展;
3)从队尾,对元素进行siftUp,保持二叉堆的平衡性;
4)向take/poll线程发送notEmpty信号;
5)释放锁lock;
6)返回true。
另外,由于PriorityBlockingQueue无界,add、put操作都直接委托给offer进行:
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一样,除了考虑并发性加锁):
1)获取公用锁lock;
2)检查队列是否为空,为空则返回null;
3)取出队列最后一个元素,从索引0开始,对其进行siftDown,保持二叉堆的平衡性;
4)释放锁lock;
5)返回队首元素值。
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return dequeue();
} finally {
lock.unlock();
}
}
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;
}
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;
}
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;
}
}
步骤(过程与PriorityQueue一样,除了考虑并发性加锁):
1)获取公用锁lock;
2)检查队列是否为空,为空则返回null;
3)释放锁lock;
4)返回队首元素值。
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (size == 0) ? null : (E) queue[0];
} finally {
lock.unlock();
}
}
不保证元素的迭代顺序,基于底层数组的副本实现:
public Iterator iterator() {
return new Itr(toArray());
}
final class Itr implements Iterator {
final Object[] array; // Array of all elements
int cursor; // index of next element to return
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
lastRet = -1;
this.array = array;
}
public boolean hasNext() {
return cursor < array.length;
}
public E next() {
if (cursor >= array.length)
throw new NoSuchElementException();
lastRet = cursor;
return (E)array[cursor++];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
removeEQ(array[lastRet]);
lastRet = -1;
}
}
若多个元素的优先级相同,则其顺序是不固定的,可以采用二级比较方法来进一步排序,以下示例为按照元素的入队顺序进行二级比较:
class FIFOEntry>
implements Comparable> {
static final AtomicLong seq = new AtomicLong(0);
final long seqNum;
final E entry;
public FIFOEntry(E entry) {
seqNum = seq.getAndIncrement();
this.entry = entry;
}
public E getEntry() { return entry; }
public int compareTo(FIFOEntry other) {
int res = entry.compareTo(other.entry);
if (res == 0 && other.entry != this.entry)
res = (seqNum < other.seqNum ? -1 : 1);
return res;
}
}
allocationSpinLock在动态扩展queue上的使用使得委托+lock是实现不了的。
PriorityBlockingQueue就是PriorityQueue的加锁线程安全版。