PriorityBlockingQueue
是一个无界队列,它没有限制,在内存允许的情况下可以无限添加元素;它又是具有优先级的队列,是通过构造函数传入的对象来判断,传入的对象必须实现comparable接口。
用法
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
PriorityBlockingQueue<Person> pbq = new PriorityBlockingQueue<>();
pbq.add(new Person(3,"person3"));
System.out.println( pbq);//[3:person3]
pbq.add(new Person(2,"person2"));
System.out.println( pbq);//[2:person2, 3:person3]
pbq.add(new Person(1,"person1"));
System.out.println( pbq);//[1:person1, 3:person3, 2:person2]
System.out.println("获取元素 " + pbq.take().getId());
System.out.println("容器为:" + pbq);//[2:person2, 3:person3]
System.out.println("获取元素 " + pbq.take().getId());
System.out.println("容器为:" + pbq);//[3:person3]
System.out.println("获取元素 " + pbq.take().getId());
System.out.println("容器为:" + pbq);//[]
}
}
class Person implements Comparable<Person>{
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public Person() {
}
@Override
public String toString() {
return this.id + ":" + this.name;
}
@Override
public int compareTo(Person person) {
return this.id > person.getId() ? 1 : ( this.id < person.getId() ? -1 :0);
}
}
对结果分析,每次添加一个元素,PriorityBlockingQueue中的person都会执行compareTo方法进行排序,但是只是把第一个元素排在首位,其他元素并没有按照顺序排序。这就保障了每次获取到的元素都是经过排序的第一个元素。
每次获取的元素都是排序后第一个元素。
成员属性
//存放数据的数组
private transient Object[] queue;
//元素个数
private transient int size;
//排序规则
private transient Comparator<? super E> comparator;
//独占锁
private final ReentrantLock lock;
//队列为空的时候的阻塞队列
private final Condition notEmpty;
入队
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);//如果cmp==null,按照key的compare()方法来排列
else
siftUpUsingComparator(n, e, array, cmp);//按照cmp排列
size = n + 1;
notEmpty.signal();//唤醒出队等待线程
} finally {
lock.unlock();
}
return true;
}
private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
Comparator<? super T> cmp) {
while (k > 0) {
int parent = (k - 1) >>> 1;//获取parent下标,parent是从0开始编号
Object e = array[parent];
if (cmp.compare(x, (T) e) >= 0)//如果compare()>=0,说明就满足排序规则了
break;
array[k] = e;
k = parent;//将parent的位置与child调换位置
}
array[k] = x;
}
比较采用往上堆化的过程。向上堆化过程也十分简单,就是如果child节点与parent节点不符合排序规则,就将child和parent换一下位置。
出队
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//可中断式申请锁
E result;
try {
while ( (result = dequeue()) == null)
notEmpty.await(); //如果队列为空,就阻塞在notEmpty队列上
} finally {
lock.unlock();
}
return result;
}
private E dequeue() {
int n = size - 1;
if (n < 0)
return null; //如果队列为空,返回null
else {
Object[] array = queue;
E result = (E) array[0];//第0个元素就是栈顶元素
E x = (E) array[n]; //将第n个元素设置为栈顶元素,然后利用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;
}
}
private static <T> 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;//右儿子下标
//如果右儿子比左儿子更满足排序规则,就将c设置为右儿子
if (right < n && cmp.compare((T) c, (T) array[right]) > 0)
c = array[child = right];
//如果两个儿子和parent相比都不满足规则,就退出循环,向下堆化结束
if (cmp.compare(x, (T) c) <= 0)
break;
array[k] = c; //如果儿子节点满足排序规则,就将儿子节点挪到父亲节点上
k = child;
}
array[k] = x;
}
}
向下堆化,就是从左儿子和右儿子之中挑出符合排序规则的点与parent调换,然后再将被调换的儿子节点作为parent节点继续执行这个过程,直至符合排序规则