生活
如果第一次你没有成功,那么称之为1.0版,继续加油。
DelayQueue的成员组成
今天来学习延时队列,这个玩意儿也是非常重要,在定时器上有用到。
首先简单了解下,延时队列就是让指定的数据再指定的时候以后出队,也就是按照时间排序,因此它的核心确实是使用了昨天看的优先队列。
下面先来看看DelayQueue的成员
//可重入锁
private transient final ReentrantLock lock = new ReentrantLock();
//优先队列
private final PriorityQueue<E> q = new PriorityQueue<E>();
//头线程。如果不为空,说明之前的线程还没等到数据,那后面的就继续等着吧
private Thread leader = null;
//条件,是否允许,到达指定时间才允许
private final Condition available = lock.newCondition();
DelayQueue创建
public DelayQueue() {}
//这里的addAll最后调用到offer,本质是遍历集合里的元素,
然后一个一个塞到优先队列尾部,这样效率不是不高吗?
很奇怪,为啥没有调用优先队列直接传入集合的构造器,
先组成一个数组,在headify..?
//另外需要注意的集合的元素必须实现Delay接口
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}
//可以看到这个接口的父接口是Comparable,这样就满足了塞入优先队列的条件
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
DelayQueue入队
入队最终就调用了offer,不存在阻塞入队,因为使用了优先队列,
该队列本身实现了扩容机制,不存在容量不足的情况。
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//调用优先队列入队
q.offer(e);
//如果根节点就是e,说明当前这个节点就是最快要出队的,唤醒等待的线程即可
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
DelayQueue出队
非阻塞出队:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
//如果根节点是空或者还不到时间,就return null
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
return null;
else
return q.poll();
} 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)
return q.poll();
//如果时间没到且有线程在前面就等待
else if (leader != null)
/
available.await();
else {
//如果时间没到,且前面没有线程,就设置自己为leader,最近一个到时间的由我获取
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//等待指定时间,唤醒后清空自己的leader去取数据
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//如果我已经清空leader并且后面还有数据,唤醒后面等待的线程
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}