持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
前言
DelayQueue 通常被称为延迟队列,在实际使用中,可以设置延迟多长时间执行,在一些延迟执行的场景中比较适合,比如说延迟转账等。
DelayQueue 底层的大致实现思路是使用锁的能力,比如延迟5s,则当前线程就会睡眠5s,当睡眠结束被唤醒后,会尝试获取资源,能获取到则会马上执行,这只是简述,当然内部实现是比较复杂的。
特点
- 队列中元素将在过期时被执行,越靠近队头,越早过期
- 未过期的元素不能够被 take
- 不允许存在空元素
DelayQueue的类定义:
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
..
}
泛型约定了,DelayQueue 中的元素必须是 Delayed 的子类,Delayed 是定义延迟能力的关键接口,并且继承了 Comparable 接口,定义了还剩多久过期的方法:
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
所以 DelayQueue 队列中的元素必须是实现 Delayed 接口的,并需要覆写 getDelay 方法和 compareTo 的方法。
put()
put方法主要是用于往DelayQueue中放入数据,源码:
public void put(E e) {
offer(e);
}
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();
}
}
put内部主要调用的是offer方法,offer方法中:
lock.lock(); 上锁
q.offer(e); 调用 PriorityQueue 的扩容、排序等方法
if (q.peek() == e) { leader = null; available.signal(); } 如果恰好刚放进去的元素正好在队列头,立马唤醒 take 的阻塞线程,执行 take 操作,如果元素需要延迟执行的话,可以使其更快的沉睡计时
lock.unlock(); 释放锁
PriorityQueue 的 offer 方法源码:
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
if (e == null) throw new NullPointerException(); 判断是否是空元素,是则抛异常
if (i >= queue.length) grow(i + 1);队列实际大小大于容量时,进行扩容(策略:如果老容量小于 64,2 倍扩容,如果大于 64,50 % 扩容)
if (i == 0) queue[0] = e;如果队列为空,当前元素正好处于队头
else { siftUp(i, e); } 如果队列不为空,需要根据优先级进行排序
siftUpComparable方法(从小到大排序)中:
while (k > 0) { } 判断k是否在当前队列实际大小的位置
int parent = (k - 1) >>> 1; 对 k 进行减倍
if (key.compareTo((E) e) >= 0) break;如果 x 比 e 大,退出,把 x 放在 k 位置上,x 比 e 小,继续循环,直到找到 x 比队列中元素大的位置