一旦一种新技术开始滚动碾压道路,如果你不能成为压路机的一部分,那么你就只能成为道路的一部分。
阻塞队列里的PriorityBlockingQueue基于PriorityQueue,所以在研究PriorityBlockingQueue之前要先研究一下PriorityQueue,这是一个有优先级概念的队列,是有顺序的,他的顺序是通过内部的比较器实现。
他的内部维护了一个数组,但不是一个简单的数组,其实是通过一个数组,维护一个二叉堆的数据结构。
因此先把二叉堆搞清楚。
研究二叉堆,有必要先把堆的概念搞清楚,注意:这里指代的堆,并不是java开发中常说的堆栈的那个堆哦。
那么,堆是一种怎么样的数据结构呢?
堆通常是一个可以看成一棵树的数组对象,有以下两个特征:
1、堆的某个节点,总是不大于或者不小于父节点
2、堆是一颗完全树。
二叉堆是一种特殊的堆,二叉树是一颗完全二叉树或者近似于完全二叉树。
二叉堆又有两种,最大堆和最小堆。
最大堆:父节点总是大于等于任何一个子节点
最小堆:父节点总是小于等于任何一个子节点。
上图是一个二叉堆(完全二叉树),他的特点是在N层被填满之前,不会开始第N+1层的填充,并且填充的顺序是从左往右。
二叉堆又可以用数组来表示:
如下图
通过上图可以发现一个规律,使用数组实现的二叉堆,位置N上的元素,其左孩子在2N+1处,其右孩子在2N+2处,根节点是0。
由于ProtityQueue是一个有优先级概念的队列,因此可以使用二叉堆来实现,队列的入队和出队,也可以通过二叉堆来实现,对应到二叉堆就是他的上移和下移,下面来图解一下 二叉堆的上移和下移。
这些图都是盗来的。。自己绘画水平太差了~~
下面图解描述一下如何向二叉堆添加一个元素:
图1是一个二叉堆 最小堆(完全二叉树)
图2在二叉树的最后插入一个节点2
图3 由于图2中2的父节点6比它大,所以2和6交换
图4 由于2的父节点5比它,所以2和5交换,
此时又是一个标准的二叉堆。
下面来看下如何把二叉堆中第一个元素移出,即优先队列中的出队操作。
图1一个二叉堆
图2准备出队最小元素1,先把最后一个元素8所在的位置删除
图3 最小节点1下两个孩子节点,取最小节点3交换
图4 最小节点1下两个孩子节点,去最小节点4交换
此时根元素1的最小孩子节点9比8还大,直接用8覆盖1。1即被删除了。
//容器数组
transient Object[] queue;
//构造器很多,总的来说指定容量和比较器,如果没有指定比较器,要求存入的元素必须实现了Compareable接口,如果没有指定容量默认11
public PriorityQueue() {
//默认11
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;
}
@SuppressWarnings("unchecked")
public PriorityQueue(Collection extends E> c) {
if (c instanceof SortedSet>) {
SortedSet extends E> ss = (SortedSet extends E>) c;
this.comparator = (Comparator super E>) ss.comparator();
initElementsFromCollection(ss);
}
else if (c instanceof PriorityQueue>) {
PriorityQueue extends E> pq = (PriorityQueue extends E>) c;
this.comparator = (Comparator super E>) pq.comparator();
initFromPriorityQueue(pq);
}
else {
this.comparator = null;
initFromCollection(c);
}
}
@SuppressWarnings("unchecked")
public PriorityQueue(PriorityQueue extends E> c) {
this.comparator = (Comparator super E>) c.comparator();
initFromPriorityQueue(c);
}
@SuppressWarnings("unchecked")
public PriorityQueue(SortedSet extends E> c) {
this.comparator = (Comparator super E>) c.comparator();
initElementsFromCollection(c);
}
public boolean offer(E e) {
//元素为空就报错
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
//容量不够就扩容
if (i >= queue.length)
//扩容
grow(i + 1);
size = i + 1;
//当是第一个元素时,就不用去比较只需要放在根节点即可
if (i == 0)
queue[0] = e;
else
//上移
siftUp(i, e);
return true;
}
//扩容
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
//如果原长度小于64,则设置新容量 原来+原来的+2
//否则 原来+原来/2
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
//上移代码
//根据是否有比较器选择不一样的方法
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
Comparable super E> key = (Comparable super E>) x;
while (k > 0) {
// 比较自己与父节点,比较不满足条件就互换
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
@SuppressWarnings("unchecked")
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;
}
public E poll() {
if (size == 0)
return null;
int s = --size;
modCount++;
//取出第一个元素return出去
E result = (E) queue[0];
//取出最后一个元素
E x = (E) queue[s];
//把最后一个元素的位置置空
queue[s] = null;
if (s != 0)
siftDown(0, x);
return result;
}
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
@SuppressWarnings("unchecked")
private void siftDownComparable(int k, E x) {
Comparable super E> key = (Comparable super E>)x;
//之所以循环一半,是因为超过一半的 2n+1就超过这个数组长度,也就是没有子节点了。
int half = size >>> 1; // loop while a non-leaf
while (k < half) {
int child = (k << 1) + 1; // assume left child is least
Object c = queue[child];
int right = child + 1;
//比较两个孩子节点,取最小或者最大的交换
if (right < size &&
((Comparable super E>) c).compareTo((E) queue[right]) > 0)
c = queue[child = right];
if (key.compareTo((E) c) <= 0)
break;
queue[k] = c;
k = child;
}
//把最后一个元素放置在对应的位置上
queue[k] = key;
}
@SuppressWarnings("unchecked")
private void siftDownUsingComparator(int k, E x) {
//同上
int half = size >>> 1;
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;
}
回到上面创建优先队列的构造器中,有一些是直接通过一个集合来创建,这里的方法是先把集合里的元素转成一个数组,然后通过heapify方法把这个数组变成一个标准的二叉堆,本质的实现原理就是下移:
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
PriorityBlockingQueue无非就死在PriorityQueue的基础上加上锁和条件变量
/**
* Lock used for all public operations
*/
private final ReentrantLock lock;
/**
* Condition for blocking when empty
*/
private final Condition notEmpty;
注意到条件变量仅有notEmpty,用以阻塞当队列为空时的出队线程。
为啥没有notFull,因为本身有扩容的操作。所以不存在容量上限的情况。
public class PBQTest {
public static void main(String[] args) {
PriorityBlockingQueue queue = new PriorityBlockingQueue();
new Thread(new PBQ(queue,new Student("小明",20))).start();
new Thread(new PBQ(queue,new Student("小红",17))).start();
new Thread(new PBQ(queue,new Student("小刚",25))).start();
new Thread(new PBQ(queue,new Student("小慌",31))).start();
new Thread(new PBQ(queue,null)).start();
new Thread(new PBQ(queue,null)).start();
new Thread(new PBQ(queue,null)).start();
new Thread(new PBQ(queue,null)).start();
}
static class PBQ implements Runnable{
private PriorityBlockingQueue queue;
private Student student;
@Override
public void run() {
if(student == null){
try {
Thread.sleep(Long.valueOf(new Random().nextInt(5000)));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
queue.offer(student);
}
}
public PBQ(PriorityBlockingQueue queue, Student student) {
this.queue = queue;
this.student = student;
}
}
static class Student implements Comparable{
@Override
public int compareTo(Student o) {
return this.age>o.age?1:0;
}
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return String.format("name:%s,age:%s",name,age);
}
}
}
name:小红,age:17
name:小明,age:20
name:小刚,age:25
name:小慌,age:31