介绍 Java PriorityBlockingQueue类
本文我们聚焦PriorityBlockingQueue类,通过实例进行学习。假设我们已经了解Queue,首先演示PriorityBlockingQueue类中元素是根据优先级排序的,接着演示这种类型队列可用于阻塞线程,最后结合两个特性在多线程环境下处理数据。
元素优先级
与标准队列不同,不能增加任意类型元素,元素必须满足两个条件:
- 实现Comparable接口元素
- 不实现Comparable接口元素,提供Comparator比较器。
实现了Comparator 或Comparable的元素,PriorityBlockingQueue中的元素是排好序的。实现比较器的目的是让最高优先级的元素排在第一位,因此当从队列中删除元素时,总是删除最高优先级的元素。
为了方便理解,我们首先在单线程中使用,不使用多个线程可以很容易证明元素被排序了:
PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>();
ArrayList<Integer> polledElements = new ArrayList<>();
queue.add(1);
queue.add(5);
queue.add(2);
queue.add(3);
queue.add(4);
queue.drainTo(polledElements);
assertThat(polledElements).containsExactly(1, 2, 3, 4, 5);
我们看到,尽管元素被随机加入,但元素却按顺序出来。这时因为Integer实现了Comparable接口,这确保元素按照升序从队列中取出。
值得注意的是,当两个元素比较结果相同,不能保证两者的顺序。
优先队列阻塞功能
如果我们处理标准队列,使用poll方法获取元素,但如果队列为空,poll方法返回null。PriorityBlockingQueue 实现了BlockingQueue接口,增加了一些额外方法用于从空队列中删除元素时发生阻塞。我们通过示例演示take方法:
PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>();
new Thread(() -> {
System.out.println("Polling...");
try {
Integer poll = queue.take();
System.out.println("Polled: " + poll);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(TimeUnit.SECONDS.toMillis(5));
System.out.println("Adding to queue");
queue.add(1);
虽然我们简单地使用sleep方法进行演示,输出结果为:
Polling...
Adding to queue
Polled: 1
证明了take方法被阻塞直到有新元素加入:
- 线程打印“Polling”证明线程启动
- 然后测试程序暂停大约5秒,证明take方法以及执行过了
- 我们给队列增加元素,几乎立刻看到“Polled:1”证明take方法返回元素
值得提及的是,BlockingQueue接口也提供了队列满时的阻塞方法。然而PriorityBlockingQueue 队列时无限制的,意味着永远不会满,总是可能增加新的元素。
演示同时使用阻塞和优先级两个特性
现在解释PriorityBlockingQueue队列的两个概念,通过示例同时使用两者进行说明。我们对前面示例进行简单修改,给队列中增加更多元素:
Thread thread = new Thread(() -> {
System.out.println("Polling...");
while (true) {
try {
Integer poll = queue.take();
System.out.println("Polled: " + poll);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(TimeUnit.SECONDS.toMillis(5));
System.out.println("Adding to queue");
queue.addAll(newArrayList(1, 5, 6, 1, 2, 6, 7));
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
我们看到首先队列线程被阻塞并等待元素加入至队列。然后给队列增加一些元素,最后显示队列按照优先顺序处理元素。输出结果如下:
Polling...
Adding to queue
Polled: 1
Polled: 1
Polled: 2
Polled: 5
Polled: 6
Polled: 6
Polled: 7
总结
本文我们演示了PriorityBlockingQueue 队列的两大特性:按优先顺序进行处理,对空队列情况,获取方法会阻塞线程。