前言
DelayQueue是BlockingQueue接口的实现类,它是带有延时功能的无界阻塞队列,意思就是每个元素都有过期时间,当从队列获取元素时,只有过期元素才会出队列。队列的头部元素是最快要过期的元素。
实现原理
先来看看它的类结构:
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
}
可以看到,存放的元素必须实现了Delayed接口,它的定义如下:
public interface Delayed extends Comparable<Delayed> {
// 返回的值代表还剩多长时间过期
long getDelay(TimeUnit unit);
}
再来看关键属性:
// 操作队列的互斥锁
private final transient ReentrantLock lock = new ReentrantLock();
// 用优先级队列来存放元素,因此上面的Delayed接口要实现Comparable接口
private final PriorityQueue<E> q = new PriorityQueue<E>();
// 基于Leader-Follower模式的变体,用于减少不必要的线程等待
private Thread leader = null;
// 互斥锁绑定的条件,目的是为了实现线程间的同步
private final Condition available = lock.newCondition();
再来看重要方法:
offer()
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 调用优先级队列q,让元素e入队
q.offer(e);
// 由于是优先级队列,头部元素不一定是刚插入的元素e,
// 如果是e的话,说明e就是最先过期的,
// 那么重置leader为null,唤醒某个等待在available条件上的take线程,
// 告知它队列头部有元素可用了
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
poll()
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
// 队列头部元素为空,或者还未到过期时间,直接返回null,非阻塞
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
take()
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(NANOSECONDS);
// 过期了直接返回
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
// 未过期且leader不为空,表示有其他线程也在take()操作,
// 当前线程则成为follower线程,在条件队列里阻塞等待
if (leader != null)
available.await();
else {
// leader为空,则将当前线程置为leader
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 等待头部元素的剩余过期时长delay
// 等待过程中会释放锁lock,其他线程可offer或take操作
available.awaitNanos(delay);
} finally {
// 线程苏醒后重置leader为null,因为下次循环first元素过期时间就到了,可以返回头部元素
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// take操作await过程中,其他线程执行了offer操作,可唤醒其他等待在available变量上的take操作线程
// 表示队列头部元素不为空了
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
参考资料:
《Java并发编程之美》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。