Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。即使无法使用 take 或 poll 移除未到期的元素。
注意:为了具有调用行为,存放到DelayDeque的元素必须继承Delayed接口。Delayed接口使对象成为延迟对象,它使存放在DelayQueue类中的对象具有了激活日期。该接口强制执行下列两个方法。
- CompareTo(Delayed o):Delayed接口继承了Comparable接口,因此有了这个方法。
- getDelay(TimeUnit unit):这个方法返回到激活日期的剩余时间,时间单位由单位参数指定。
概述:添加到队列的元素实现了Delayed接口的比较方法和获得延迟时间方法,实际添加元素到了PriorityQueue队列并根据优先级进行了排序。take在获取队列元素时判断元素的延迟时间是否到期,未到期进行阻塞,到期才能获取元素。
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>();//存放元素的队列,采用了优先级队列
public boolean add(E e) {
return offer(e);
}
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();//获取但不移除队列元素
q.offer(e);//添加元素到队列,队列会根据元素的比较方法排序
if (first == null || e.compareTo(first) < 0)//如果原来队列为空或者现添加元素的比头元素优先,把阻塞队列激活
available.signalAll();
return true;
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null) {//如果队列为空,阻塞
available.await();
} else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);//获取元素的延迟时间
if (delay > 0) {//如果延迟时间>0说明还未到期,进行wait delay时间段后,自动激活
long tl = available.awaitNanos(delay);
} else {//延迟时间<=0,说明已经到期,进行出队
E x = q.poll();
assert x != null;
if (q.size() != 0)
available.signalAll(); // wake up other takers
return x;
}
}
}
} finally {
lock.unlock();
}
}
总结:take阻塞原理:通过peek()获得队列头,进行判断队列为空进行阻塞,如果队列不为空,判断队列头的getDelay()延迟时间是否<0如果小于说明到期进行poll取出队列,如果延迟时间>0说明未到期,通过awaitNanos(delay)阻塞只到延迟时间。
//和take方法相同,只是在队列为空时,返回null不进行阻塞。
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null) {
if (nanos <= 0)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay > 0) {
if (nanos <= 0)
return null;
if (delay > nanos)
delay = nanos;
long timeLeft = available.awaitNanos(delay);
nanos -= delay - timeLeft;
} else {
E x = q.poll();
assert x != null;
if (q.size() != 0)
available.signalAll();
return x;
}
}
}
} finally {
lock.unlock();
}
}
take 取