队列(二)——优先队列PriorityQueue
队列通常采用FIFO(先进先出)策略,可以满足多数情况下的开发需求,但是也有其不足之处。
例如:
- 我要小明帮我买早餐,
- 第一次,“小明,你后天帮我买牛奶吧!”,
- 过了一会儿,“小明,你明天帮我买豆浆呗!”。
这个案例中,我先叫小明买的牛奶,再叫他买的豆浆,如果按照普通队列,按顺序执行,先买牛奶显然是错的。
优先队列
优先队列满足了上述的开发需求,队列中的元素按照自然顺序(Comparator)进行排序,通过Comparable接口可以定制任意的优先策略。
常见的优先队列有:
- PriorityQueue<E> 优先队列
- PriorityBlockingQueue<E> 阻塞优先队列(线程安全的队列)
- DelayQueue<E extends Delayed> 延迟队列(函数使用了可重入锁,是线程安全的队列),延迟队列所有元素必须继承Delayed接口,Delayed接口又继承自Comparable接口,专门用于时间的比较。
这三者优先队列之间不存在继承关系,DelayQueue内部包含PriorityQueue实例,是一个优先队列的装饰者;PriorityBlockingQueue源码也不同与另外两个。
使用Demo
复用上一个文章的工具类,通过PriorityBlockingQueue实现上述中买早餐的案例,顺便展示Delayed接口的使用方法。
package com.sea.common.util; import java.util.concurrent.Delayed; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; class DelayItem implements Delayed { private long milli; private String msg; @Override public String toString() { return msg; } public DelayItem(long milli, String msg) { this.milli = milli; this.msg = msg; } @Override public long getDelay(TimeUnit unit) { /** * TimeUnit提供了时间转换的算法,如果确定了延迟的精度,直接return milli即可 */ return unit.convert(milli, TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { return this.milli == o.getDelay(TimeUnit.NANOSECONDS) ? 0 : (this.milli > o.getDelay(TimeUnit.NANOSECONDS) ? 1 : -1); } } public class Test { public static void main(String[] args) throws InterruptedException { Handler<DelayItem> handler = new Handler<DelayItem>() { @Override public void handleMessage(DelayItem msg) throws Exception { Thread.sleep(1000); System.out.println(msg); } }; BlockingLopper<DelayItem> lopper = new BlockingLopper<DelayItem>(); lopper.setDummy(new DelayItem(Long.MAX_VALUE, "")); lopper.setHandler(handler); lopper.setQueue(new PriorityBlockingQueue<>(2)); lopper.loop(); lopper.offer(new DelayItem(200, "小明,你后天帮我买牛奶吧!")); lopper.offer(new DelayItem(100, "小明,你明天帮我买豆浆呗!")); System.out.println("--------------------"); lopper.stopLoop(); } }
内容补充
在API中还有几个比较抢眼的名词,因为不是十分常用,这里简单介绍。
- ConcurrentLinkedQueue同步线性队列,一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。针对线程安全问题,java.util.concurrent 包下实现了多个线程安全的Collection和Map,名字以Concurrent开头,util包下有对应的非线程安全实现类,ConcurrentLinkedQueue与LinkedList功能相对应。
- Deque<E> 一个线性的 collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。Deque看似更加灵活,然而实际开发远不及普通队列有用。
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!