延时队列
插入时带时间,到时间可取出,比较典型的的二叉堆使用场景–每次获取只需头节点,插入波动大,无需查看排序.
此处队列是线程安全的.全程覆盖读写锁.
放入节点
/**
* Inserts the specified element into this delay queue.
*
* @param e the element to add
* @return {@code true}
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// q=>PriorityQueue(优先级队列)
q.offer(e);
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
无阻赛的获取,没有直接放弃
/**
* Retrieves and removes the head of this queue, or returns {@code null}
* if this queue has no elements with an expired delay.
*
* @return the head of this queue, or {@code null} if this
* queue has no elements with an expired delay
*/
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 是否有头结点
E first = q.peek();
// 有就返回
return (first == null || first.getDelay(NANOSECONDS) > 0)
? null
: q.poll();
} finally {
lock.unlock();
}
}
阻塞的获取节点
/**
* Retrieves and removes the head of this queue, waiting if necessary
* until an element with an expired delay is available on this queue.
*
* @return the head of this queue
* @throws InterruptedException {@inheritDoc}
*/
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 可打断型lock
lock.lockInterruptibly();
try {
for (;;) {
// 头结点
E first = q.peek();
// 没有直接阻塞
// available=>ReentrantLock中condition 锁
if (first == null)
available.await();
else {
// 有节点 判定是否到期能取出
long delay = first.getDelay(NANOSECONDS);
// 可用直接取出
if (delay <= 0L)
return q.poll();
first = null; // don't retain ref while waiting
// 头结点占用线程
if (leader != null)
available.await();
else {
// 将获取线程设置为使用头结点线程
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 等到下一个节点
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
这里只截取使用二叉堆的