Java优先级队列调研
介绍:
基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的
Comparator
进行排序,具体取决于所使用的构造方法。优先级队列不允许使用
null
元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象。
此队列的头 是按指定排序方式确定的最小 元素。如果多个元素都是最小值,则头是其中一个元素——选择方法是任意的。
优先级队列是无界的,但是有一个内部容量控制着用于存储队列元素的数组大小,它通常至少等于队列的大小。
随着不断向优先级队列添加元素,其容量会自动增加。无需指定容量增加策略的细节。
虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败。
1、PriorityQueue
非同步队列。
在多线程同时读写时会出现异常。
2、PriorityBlockingQueue
同步队列。
适用于多线程环境中。
3、实现方式:
说明:注释掉的PriorityQueue部分是测试PriorityQueue用,因为在多线程时有同步问题,可能在开发中不适用。
①队列中元素用
Comparator
进行排序 要自己实现
Comparator
接口类。添加进优先队列中的数据,Queue会调用Comparator进行比较,小的数据会放在Queue的前部,反之,放在后部。最小的数据会放在Queue的头部。(具体实现?) 放在Queue中的数据只有添加时才进行比较,加入后的位置是不会改变的——所以元素比较用的值最好是固定的,否则取出的数据有可能不是最优先的。
Comparators实现代码:
Comparator实现类
import java.util.Comparator; public class Comparators implements Comparator<Executor> { @Override public int compare(Executor paramT1, Executor paramT2) { return (int)(paramT1.getDeadline() - paramT2.getDeadline()); } }
Executor实现代码:
import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Date; public class Executor implements Runnable{ private long createTime; private long timeout; private OutputStreamWriter writer; public Executor(long timeout, OutputStreamWriter writer){ this.timeout = timeout; createTime = new Date().getTime(); this.writer = writer; } @Override public void run() { try { writer.write("createTime>>[" + createTime + "] timeout>>[" + timeout + "] hashcode>>[" + (createTime + timeout) + "]\r\n"); } catch (IOException e) { e.printStackTrace(); } } public long getDeadline(){ return this.createTime + this.timeout; } }
写入文件的原因是多线程写入会保证写入的顺序,基本可以保证取得的数据的顺序是Queue中的顺序。
测试代码
class Consumer implements Runnable { private AtomicInteger counter; private PriorityBlockingQueue<Executor> runQueue; public Consumer(PriorityBlockingQueue<Executor> runQueue, AtomicInteger counter){ // private PriorityQueue<Executor> runQueue; // public Consumer(PriorityQueue<Executor> runQueue, AtomicInteger counter){ this.runQueue = runQueue; this.counter = counter; } @Override public void run() { Executor exe = null; while((exe = runQueue.poll()) != null){ exe.run(); counter.decrementAndGet(); } } } class Producer implements Runnable { private AtomicInteger counter; private OutputStreamWriter writer; private PriorityBlockingQueue<Executor> runQueue; public Producer(PriorityBlockingQueue<Executor> runQueue, AtomicInteger counter, OutputStreamWriter writer){ // private PriorityQueue<Executor> runQueue; // public Producer(PriorityQueue<Executor> runQueue, AtomicInteger counter, // OutputStreamWriter writer){ this.runQueue = runQueue; this.counter = counter; this.writer = writer; } @Override public void run() { Random rand = new Random(); for(int i=0; i < 1000; i++){ long timeout = rand.nextInt(10000000); Executor exe = new Executor(timeout, writer); runQueue.offer(exe); counter.incrementAndGet(); } } } //测试函数代码 public static void TestWithComparator() throws InterruptedException, IOException{ AtomicInteger counter = new AtomicInteger(); // PriorityQueue<Executor> runQueue = new PriorityQueue<Executor>(1000); PriorityBlockingQueue<Executor> runQueue = new PriorityBlockingQueue<Executor>(1000, new Comparators()); OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("e:\\priorityQueue.txt")); ExecutorService producerPool = Executors.newFixedThreadPool(16); long start = System.nanoTime(); for(int i=0; i < 1000; i++){ producerPool.execute(new Producer(runQueue, counter, writer)); } producerPool.shutdown(); while(!producerPool.isTerminated()){ } long end = System.nanoTime(); System.out.println("comparator add time is " + (end - start)); ExecutorService consumerPool = Executors.newFixedThreadPool(16); start = System.nanoTime(); for(int i=0; i < 16; i++){ consumerPool.execute(new Consumer(runQueue, counter)); } consumerPool.shutdown(); while(!consumerPool.isTerminated()){ ; } end = System.nanoTime(); System.out.println("comparator exe time is " + (end - start)); System.out.println(counter.get()); writer.close(); }
②队列中元素实现Comparable接口进行排序
添加入队列中的元素要实现Comparable接口,排序时调用实现Comparable接口的compareTo方法进行比较,排序的结果同①。
ExecutorCmp
import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Date; public class ExecutorCmp implements Runnable, Comparable<ExecutorCmp>{ private long createTime; private long timeout; private OutputStreamWriter writer; public ExecutorCmp(long timeout, OutputStreamWriter writer){ this.timeout = timeout; createTime = new Date().getTime(); this.writer = writer; } @Override public void run() { try { writer.write("createTime>>[" + createTime + "] timeout>>[" + timeout + "] hashcode>>[" + (createTime + timeout) + "]\r\n"); } catch (IOException e) { e.printStackTrace(); } } @Override public int compareTo(ExecutorCmp arg0) { return (int)((this.createTime + this.timeout) - (arg0.createTime + arg0.timeout)); } }
Comparable测试代码
class CmpConsumer implements Runnable { private AtomicInteger counter; private PriorityBlockingQueue<ExecutorCmp> runQueue; public CmpConsumer(PriorityBlockingQueue<ExecutorCmp> runQueue, AtomicInteger counter){ // private PriorityQueue<Executor> runQueue; // public Consumer(PriorityQueue<Executor> runQueue, AtomicInteger counter){ this.runQueue = runQueue; this.counter = counter; } @Override public void run() { ExecutorCmp exe = null; while((exe = runQueue.poll()) != null){ exe.run(); counter.decrementAndGet(); } } } class CmpProducer implements Runnable { private AtomicInteger counter; private OutputStreamWriter writer; private PriorityBlockingQueue<ExecutorCmp> runQueue; public CmpProducer(PriorityBlockingQueue<ExecutorCmp> runQueue, AtomicInteger counter, OutputStreamWriter writer){ // private PriorityQueue<Executor> runQueue; // public Producer(PriorityQueue<Executor> runQueue, AtomicInteger counter, // OutputStreamWriter writer){ this.runQueue = runQueue; this.counter = counter; this.writer = writer; } @Override public void run() { Random rand = new Random(); for(int i=0; i < 1000; i++){ long timeout = rand.nextInt(10000000); ExecutorCmp exe = new ExecutorCmp(timeout, writer); runQueue.offer(exe); counter.incrementAndGet(); } } } //测试函数代码: public static void testWithComparable() throws InterruptedException, IOException{ AtomicInteger counter = new AtomicInteger(); PriorityBlockingQueue<ExecutorCmp> runQueue = new PriorityBlockingQueue<ExecutorCmp>(1000); OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("e:\\priorityQueue1.txt")); ExecutorService producerPool = Executors.newFixedThreadPool(16); long start = System.nanoTime(); for(int i=0; i < 1000; i++){ producerPool.execute(new CmpProducer(runQueue, counter, writer)); } producerPool.shutdown(); while(!producerPool.isTerminated()){ } long end = System.nanoTime(); System.out.println("comparatable add time is " + (end - start)); ExecutorService consumerPool = Executors.newFixedThreadPool(16); start = System.nanoTime(); for(int i=0; i < 16; i++){ consumerPool.execute(new CmpConsumer(runQueue, counter)); } consumerPool.shutdown(); while(!consumerPool.isTerminated()){ ; } end = System.nanoTime(); System.out.println("comparatable exe time is " + (end - start)); System.out.println(counter.get()); writer.close(); }
4、在ThreadPoolExecutor中的应用方式
在ThreadPoolExecutor中使用队列是为了保存接收但是暂时无法处理的任务(Runnable),如果要使用PriorityBlockingQueue保存这些Runnable,必须对Runnable进行封装,并实现Comparable接口。
代码如下:
class SafTask implements Runnable, Comparable<SafTask> { private int order; public SafTask(int order){ this.order = order; } public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(order); } @Override public int compareTo(SafTask other) { return this.order - other.order; } } //测试代码 BlockingQueue<Runnable> configQueue = new PriorityBlockingQueue<Runnable>(); ThreadPoolExecutor stpe = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS, configQueue); Random rand = new Random(); for(int i = 0; i < 1000; i++){ int order = rand.nextInt(1000); Thread.sleep(3); stpe.execute(new SafTask(order)); } stpe.shutdown();
结论:
1、使用PriorityBlockingQueue时元素实现Comparable接口进行排序性能上会高一些:
comparator add time is 250168728comparator exe time is 2755292150
*********************************************************************************
comparatable add time is 218674112
comparatable exe time is 2222345324
2、在ThreadPoolExecutor中使用PriorityBlockingQueue时,要封装Runnable并实现Comparable接口