阻塞队列BlockingQueue(三)--DelayQueue
DelayQueue
支持延时获取元素的无界阻塞队列
内部采用PriorityQueue与ReentrantLock实现。
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private transient final ReentrantLock lock = new ReentrantLock();
private transient final Condition available = lock.newCondition();
private final PriorityQueue<E> q = new PriorityQueue<E>();
...
}
队列中元素必须实现Delayed接口,Delayed接口又继承了Comparable接口,原因在于DelayQueue内部元素需要排序
,一般情况按过期时间优先级排序。
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
应用场景
- 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
- 定时任务调度:使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,比如TimerQueue就是使用DelayQueue实现的。
实现Delayed接口
第一步:在对象创建的时候,初始化基本数据。使用time记录当前对象延迟到什么时候可以使用,使用sequenceNumber来标识元素在队列中的先后顺序。
private static final AtomicLong sequencer = new AtomicLong(0);
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}
第二步:实现(Delayed 接口的)getDelay方法返回当前元素还需要延时多长时间,单位是纳秒。
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), TimeUnit.NANOSECONDS);
}
第三步:如何实现compareTo方法来指定元素的顺序。例如,让延时时间最长的放在队列的末尾。
如何实现延时阻塞队列
当消费者从队列里获取元素时,如果元素没有达到延时时间,就阻塞当前线程。
leader变量是一个等待获取队列头部元素的线程。
- 如果leader!=null,表示已经有线程在等待获取队列的头元素。所以,使用await()方法让当前线程等待信号。
- 如果leader==null,则把当前线程设置成leader,并使用awaitNanos()方法让当前线程等待接收信号或等待delay时间。