Hydra:末尾的时分你本人不都说了吗,明天我们聊的DelayQueue和前几天聊过的PriorityBlockingQueue多少有点关系。DelayQueue的底层是PriorityQueue,而PriorityBlockingQueue和它的差别也没有多少,只是在PriorityQueue的基础上加上锁和条件等候,入队和出队用的都是二叉堆的那一套逻辑。底层运用的有这些:
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
private Thread leader = null;
private final Condition available = lock.newCondition();
面试官:你这样也有点太糊弄我了吧,这就把我敷衍过去了?
Hydra:还没完呢,还是先看入队的offer办法,它的源码如下:
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e);
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
DelayQueue每次向优先级队列PriorityQueue中添加元素时,会以元素的剩余延迟时间delay作为排序的要素,来完成使最先过时的元素排在队首,以此到达在之后从队列中取出的元素都是先取出最先抵达过时的元素。
二叉堆的结构进程我们上次讲过了,就不再重复了。向队列中添加完5个元素后,二叉堆和队列中的结构是这样的:
当每个元素在按照二叉堆的顺序插入队列后,会查看堆顶元素能否刚插入的元素,假设是的话那么设置leader线程为空,并唤醒在available上阻塞的线程。
这里先复杂的引见一下leader线程的作用,leader是等候获取元素的线程,它的作用主要是用于增加不必要的等候,详细的运用在前面引见take办法的时分我们细说。
面试官:也别一会了,趁热打铁直接讲队列的出队办法吧。
Hydra:这还真没法着急,在看阻塞办法take前还得先看看非阻塞的poll办法是如何完成的:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
代码十分短,了解起来十分复杂,在加锁后首先反省堆顶元素,假设堆顶元素为空或没有到期,那么直接前往空,否则前往堆顶元素,然后解锁。
面试官:好了,铺垫完了吧,该讲阻塞办法的进程了吧?
Hydra:阻塞的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 {
(责任编辑:admin)