最后一个线程池的阻塞队列了,写完这个线程池篇应该算是结束了。
PriorityBlockingQueue 队列是 JDK1.5 的时候出来的一个阻塞队列。但是该队列有个入队的时候是不会阻塞的,永远会加到队尾。下面我们介绍下它的几个特点:
- PriorityBlockingQueue 和 ArrayBlockingQueue 一样是基于数组实现的,但后者在初始化时需要指定长度,前者默认长度是 11。
- 该队列可以说是真正的无界队列,它在队列满的时候会进行扩容,而前面说的无界阻塞队列其实都有有界,只是界限太大可以忽略(最大值是 2147483647)
- 该队列属于权重队列,可以理解为它可以进行排序,但是排序不是从小到大排或从大到小排,是基于数组的堆结构(具体如何排下面会进行分析)
- 出队方式和前面的也不同,是根据权重来进行出队,和前面所说队列中那种先进先出或者先进后出方式不同。
注意:
-
堆结构实际上是一种完全二叉树,建议学习前了解一下二叉树。
-
堆又分为 大顶堆 和 小顶堆。大顶堆中第一个元素肯定是所有元素中最大的,小顶堆中第一个元素是所有元素中最小的。
下面是 PriorityBlockingQueue 队列的类图:
阻塞队列基本都会实现 BlockingQueue 接口,就不介绍。下面我们直接进入源码来进行分析学习。先看看类中的几个字段:
/**
* 默认数组长度
*/
private static final int DEFAULT_INITIAL_CAPACITY = 11;
/**
* 最大达容量,分配时超出可能会出现 OutOfMemoryError 异常
*/
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;
/**
* 空队列时进行线程阻塞的 Condition 对象
*/
private final Condition notEmpty;
从上面的字段我们可以知道,该队列可以排序,使用显示锁来保证操作的原子性,在空队列时,出队线程会堵塞等。那大概可以猜测到会有哪些构造方法了。
/**
* 默认构造,使用长度为 11 的数组,比较器为空
*/
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();
this.notEmpty = lock.newCondition();
this.comparator = comparator;
this.queue = new Object[initialCapacity];
}
下面我们来看下队列操作的核心方法。
入队方法:
下面可以看到 put 方法最终会调用 offer 方法,所以我们只看 offer 方法即可。
public void put(E e)