目录
- 一、什么ScheduledThreadPoolExecutor
- 二、使用样例
- 三、应用场景
- 四、相关知识
- 五、考察点
- 六、源码解析
- 6.1 ScheduledThreadPoolExecutor类
- 6.1.1 成员变量
- 6.1.2 scheduleAtFixedRate将任务以固定周期执行
- 6.1.3 canRunInCurrentRunState判断当前状态能否运行
- 6.1.4 delayExecute延时执行,提交任务并预启动Worker
- 6.1.5 reExecutePeriodic任务执行结束后,重新将任务添加到阻塞队列
- 6.1.6 onShutdown父类的shutdown会调用,线程池关闭时的动作
- 6.1.7 decorateTask装饰任务
- 6.1.8 triggerTime 计算delay的值
- 6.1.9 overflowFree防止compareTo中减法发生溢出
- 6.1.10 schedule提交延时但非周期性任务
- 6.1.11 scheduleWithFixedDelay延时周期任务,以固定延时执行
- 6.2 内部类ScheduledFutureTask
- 6.3 内部类DelayedWorkQueue
一、什么ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor调度线程池执行器,继承了ThreadPoolExecutor,除了线程池的特性外,还有延迟执行、定期执行任务的特点。
二、使用样例
2.1 关键方法
2.1.1 submit/execute 提交任务
这个方法和普通的线程池一样,执行完就结束了。无延迟或周期调用的功能。
2.1.2 scheduleAtFixedRate以固定周期时间执行
创建一个周期性的任务,第一次执行的延期时间是initialDelay
- 之后每隔period执行一次,不等待第一次执行完就开始计时
- 如果任务的执行时间过长,两次执行的开始时间可能会大于周期时间
代码样例1:
任务执行时间为5s,使用延时时间1s,周期6s的参数执行。根据结果可以发现线程池第一次执行时间是提交线程1s后(延时时间),启动后开始计时,6s后再次执行当前任务。
private static void testScheduleAtFixedRate() {
System.out.println(LocalDateTime.now());
executor.scheduleAtFixedRate(() -> {
System.out.println("scheduleAtFixedRate-start-" + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("scheduleAtFixedRate-end-" + LocalDateTime.now());
}, 1, 6, TimeUnit.SECONDS);
}
结果:
2022-05-23T20:15:30.135
scheduleAtFixedRate-start-2022-05-23T20:15:31.143
scheduleAtFixedRate-end-2022-05-23T20:15:36.143
scheduleAtFixedRate-start-2022-05-23T20:15:37.142
scheduleAtFixedRate-end-2022-05-23T20:15:42.143
scheduleAtFixedRate-start-2022-05-23T20:15:43.141
代码样例2:
任务执行时间为5s,使用延时时间1s,周期1s的参数执行。根据结果可以发现线程池第一次执行时间是提交线程1s后(延时时间),启动后开始计时,5s后再次执行当前任务。
说明即使周期为1s,但上个任务结束时间超过了下次任务执行开始时间,则下次任务开始时间推迟到上次任务结束时间。
下次任务执行开始时间 = MAX(首次任务开始时间+周期时间*次数,上次任务结束时间)
private static void testScheduleAtFixedRateOver() {
System.out.println(LocalDateTime.now());
executor.scheduleAtFixedRate(() -> {
System.out.println("scheduleAtFixedRate-start-" + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("scheduleAtFixedRate-end-" + LocalDateTime.now());
}, 1, 1, TimeUnit.SECONDS);
}
结果:
2022-05-23T20:18:40.084
scheduleAtFixedRate-start-2022-05-23T20:18:41.090
scheduleAtFixedRate-end-2022-05-23T20:18:46.091
scheduleAtFixedRate-start-2022-05-23T20:18:46.091
scheduleAtFixedRate-end-2022-05-23T20:18:51.091
scheduleAtFixedRate-start-2022-05-23T20:18:51.091
scheduleAtFixedRate-end-2022-05-23T20:18:56.091
scheduleAtFixedRate-start-2022-05-23T20:18:56.091
2.1.3 scheduleWithFixedDelay以固定延迟执行
创建一个周期执行的任务,
- 第一次执行时间为initialDelay
- 第一次执行完后延迟delay后开始下一次执行
代码样例:
任务执行时间为5s,使用初始延时时间1s,延时2s的参数执行。根据结果可以发现线程池第一次执行时间是提交线程1s后(初始延时时间),又过了7s(5+2)执行了下一个任务。
下次任务执行时间 = 上次任务执行结束时间 + 延时时间
private static void testScheduleWithFixedDelay() {
System.out.println(LocalDateTime.now());
executor.scheduleWithFixedDelay(() -> {
System.out.println("scheduleAtFixedRate-start-" + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("scheduleAtFixedRate-end-" + LocalDateTime.now());
}, 1, 2, TimeUnit.SECONDS);
}
结果:
2022-05-23T20:21:59.420
scheduleAtFixedRate-start-2022-05-23T20:22:00.427
scheduleAtFixedRate-end-2022-05-23T20:22:05.427
scheduleAtFixedRate-start-2022-05-23T20:22:07.429
scheduleAtFixedRate-end-2022-05-23T20:22:12.430
scheduleAtFixedRate-start-2022-05-23T20:22:14.432
三、应用场景
Spring的@Scheduled注解实现定时任务或者延时任务
看Spring源码,描述流程
四、相关知识
4.1 流程图
4.2 体系结构
4.3 比较
Timer
五、考察点
六、源码解析
6.1 ScheduledThreadPoolExecutor类
6.1.1 成员变量
/**
* 处于shutdown状态,已经处于阻塞队列的任务能否继续执行
*/
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
/**
* 处于shutdown状态,已经处于阻塞队列的任务能否继续执行
*/
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
/**
* 如果取消的话,从队列中移除任务。
* 因为可能是周期任务,只取消本次执行,不取消周期执行
*/
private volatile boolean removeOnCancel = false;
/**
* 为相同延时的任务提供的顺序编号,保证进入阻塞队列的任务之间FIFO的顺序
*/
private static final AtomicLong sequencer = new AtomicLong();
6.1.2 scheduleAtFixedRate将任务以固定周期执行
/**
* 创建一个周期性的任务,第一次执行的延期时间是initialDelay
* 之后每隔period执行一次,不等待第一次执行完就开始计时
* 如果任务的执行时间过长,两次执行的开始时间会大于周期时间
*
* 举例:设置执行时间点为 1(初始延时),4(间隔为3)7,10,13
* 假如第一次执行时间为4,第二次执行时间为1,则执行的时间点位
* 1,5,7,10,13
* @param command 任务
* @param initialDelay 第一次延期时间
* @param period 周期间隔
* @param unit 单位
* @return 周期任务
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
// 创建一个周期任务,延时时间为initialDelay
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
// 装饰任务
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
// 当前任务执行完重新入队的任务
sft.outerTask = t;
// 延时执行
delayedExecute(t);
return t;
}
有三个关键的地方:
- 创建ScheduledFutureTask对象
- 设置下个周期执行的任务
- 将ScheduledFutureTask对象加入阻塞队列并预启动一个Worker线程
ScheduledFutureTask继承FutureTask,实现了Runnable方法,因此其对象作为任务提交到阻塞队列,并且被线程取出来后,执行的是run方法。
下个周期的任务是t,t由decorateTask对上个任务进行扩展,这里默认不做扩展。
延迟执行delayExecute,先将任务加到阻塞队列里,然后预启动一个线程,等待将来线程池中的Worker从阻塞队列中获取任务。
6.1.3 canRunInCurrentRunState判断当前状态能否运行
用在任务已提交,但线程池已关闭(shutdown)的场景。
/**
* 判断当前状态是否能运行
* @param periodic 是否为周期性的任务
* @return 是否能够运行
*/
boolean canRunInCurrentRunState(boolean periodic) {
return isRunningOrShutdown(periodic ?
// 如果是周期性的,返回判断关闭后能否执行周期性任务
continueExistingPeriodicTasksAfterShutdown :
// 否则返回判断能否执行延迟任务
executeExistingDelayedTasksAfterShutdown);
}
6.1.4 delayExecute延时执行,提交任务并预启动Worker
/**
* 延时执行
* @param task 任务
*/
private void delayedExecute(RunnableScheduledFuture<?> task) {
// 线程池关闭则拒绝任务
if (isShutdown())
reject(task);
else {
// 直接放到延时阻塞队列里
super.getQueue().add(task);
// 再判断是否关闭,状态>=SHUTDOWN
if (isShutdown() &&
// 当前状态不允许运行任务
!canRunInCurrentRunState(task.isPeriodic()) &&
// 移除任务成功
remove(task))
// 不中断地取消
task.cancel(false);
else
// 预启动一个线程
ensurePrestart();
}
}
6.1.5 reExecutePeriodic任务执行结束后,重新将任务添加到阻塞队列
/**
* 重排队一个周期任务
* 不会拒绝策略而是直接丢掉任务
* @param task the task
*/
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
// 判断周期性任务在当前状态能否执行
if (canRunInCurrentRunState(true)) {
// 阻塞队列加入该任务
super.getQueue().add(task);
// 如果当前装填不能运行周期性任务 并且 移除阻塞队列成功
if (!canRunInCurrentRunState(true) && remove(task))
// 不中断地取消任务
task.cancel(false);
else
// 预启动一个线程
ensurePrestart();
}
}
6.1.6 onShutdown父类的shutdown会调用,线程池关闭时的动作
/**
* 父类的shutdown会调用
* 关闭线程池时对两种任务的处理
*/
@Override
void onShutdown() {
BlockingQueue<Runnable> q = super.getQueue();
boolean keepDelayed =
getExecuteExistingDelayedTasksAfterShutdownPolicy();
boolean keepPeriodic =
getContinueExistingPeriodicTasksAfterShutdownPolicy();
// 如果关闭后,延迟和定期执行的任务都不再执行,则全部任务取消
if (!keepDelayed && !keepPeriodic) {
for (Object e : q.toArray())
if (e instanceof RunnableScheduledFuture<?>)
((RunnableScheduledFuture<?>) e).cancel(false);
q.clear();
} else {
// Traverse snapshot to avoid iterator exceptions
for (Object e : q.toArray()) {
if (e instanceof RunnableScheduledFuture) {
RunnableScheduledFuture<?> t =
(RunnableScheduledFuture<?>) e;
if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||
t.isCancelled()) { // also remove if already cancelled
if (q.remove(t))
t.cancel(false);
}
}
}
}
tryTerminate();
}
6.1.7 decorateTask装饰任务
/**
* 用户可自定义的扩展方法,用于装饰任务
*/
protected <V> RunnableScheduledFuture<V> decorateTask(
Runnable runnable, RunnableScheduledFuture<V> task) {
return task;
}
6.1.8 triggerTime 计算delay的值
/**
* 如果delay小于Long最大值的一半,则直接返回计算结果
* 否则需要计算delay的值
*/
long triggerTime(long delay) {
// now + delay or Long + time
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
6.1.9 overflowFree防止compareTo中减法发生溢出
/**
*
* 防止compareTo中减法发生溢出
* 如果delay - 堆顶延迟时间 发生了溢出,就将delay设置为 Long + headDelay
* delay值变小了
*/
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
// time - now
long headDelay = head.getDelay(NANOSECONDS);
// 堆顶的任务已经到期了
if (headDelay < 0 && (delay - headDelay < 0))
// Long + time -now
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
6.1.10 schedule提交延时但非周期性任务
/**
* 定时执行
* @param command 任务
* @param delay 延迟时间
* @param unit 单位
* @return 结果
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
// 装饰任务
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
// 延迟执行
delayedExecute(t);
// 返回Future
return t;
}
6.1.11 scheduleWithFixedDelay延时周期任务,以固定延时执行
/**
* 创建一个周期执行的任务,第一次执行时间为initialDelay
* 第一次执行完后延迟delay后开始下一次执行
* @param command 任务
* @param initialDelay 第一次执行时间
* @param delay 延时
* @param unit 单位
* @return 任务结果
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
// 这里delay传的是负值
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
6.2 内部类ScheduledFutureTask
6.2.1 内部类的成员变量
/**
* 两个任务有相同延时时间时,按照FIFO入队,该变量是为相同延时任务提供的顺序编号
*/
private final long sequenceNumber;
/**
* 任务执行的时刻。任何一个时间点都可以用long去表示
*/
private long time;
/**
* 正数代表固定速率执行,为scheduleAtFixedRate提供服务
* 负数代表固定延时执行,为scheduleWithFixedDelay提供服务
* 0代表不重复执行
*/
private final long period;
/**
* reExecutePeriodic执行后重新入队的任务
*/
RunnableScheduledFuture<V> outerTask = this;
/**
* 延迟队列的索引,支持快速取消,-1代表不在队列中
*/
int heapIndex;
6.2.2 getDelay获取任务还需要多久执行
/**
* 获取任务的剩余等待时间
* @param unit 单位
* @return 剩余等待时间
*/
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), NANOSECONDS);
}
final long now() {
return System.nanoTime();
}
6.2.3 compareTo用于阻塞队列中元素的排序
/**
* 比较两个延时任务的延时时间,如果相同使用序列号比较
* @param other 被比较的元素
* @return 比较结果
*/
public int compareTo(Delayed other) {
if (other == this) // compare zero if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>) other;
long diff = time - x.time;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else if (sequenceNumber < x.sequenceNumber)
return -1;
else
return 1;
}
long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
6.2.4 isPeriodic是否周期性任务
/**
* 判断是否为周期性的任务
* period为0代表只执行一次
*/
public boolean isPeriodic() {
return period != 0;
}
6.2.5 setNextRunTime设置下次执行任务的时间
是 scheduleAtFixedRate和scheduleWithFixedDelay功能区分的关键代码
/**
* 对于周期性的任务,设置下一次运行时间
* 这里是 scheduleAtFixedRate和scheduleWithFixedDelay区分的关键代码
*/
private void setNextRunTime() {
long p = period;
// 如果p大于0,则下次执行任务的时间是上次任务的开始时间+执行周期
if (p > 0)
time += p;
else
// 如果p小于等于0,则下次执行时间是上次任务结束时间+执行周期
time = triggerTime(-p);
}
6.2.6 cancel取消任务
复用了父类的取消逻辑
/**
* 取消任务
* @param mayInterruptIfRunning 是否中断
* @return 取消成功
*/
public boolean cancel(boolean mayInterruptIfRunning) {
// 取消任务,将会将任务从阻塞中唤醒并结束,返回结果为null,判断是否取消成功,
boolean cancelled = super.cancel(mayInterruptIfRunning);
// 取消成功,并且 取消时需要从阻塞队列移除 并且 延迟队列的索引大于等于0
if (cancelled && removeOnCancel && heapIndex >= 0)
// 从阻塞队列中移除当前任务
remove(this);
// 返回最终是否移除成功
return cancelled;
}
6.2.7 run任务提交后的执行逻辑
如果是周期性的任务,在当前任务执行后会设置下次执行时间,并重新进入阻塞队列。
/**
* 重写FutureTask的方法,提交到线程池时执行的是该方法的逻辑
*/
public void run() {
// 判断是否是周期性的任务
boolean periodic = isPeriodic();
// 如果当前状态不能运行
if (!canRunInCurrentRunState(periodic))
// 取消当前任务
cancel(false);
// 如果不是周期性的任务(只执行一次)
else if (!periodic)
// 直接启动任务
ScheduledFutureTask.super.run();
// 如果是周期性的任务,并且运行结束后状态重置成功(中间运行抛出异常或者被取消了则返回失败)
else if (ScheduledFutureTask.super.runAndReset()) {
// 设置下一次运行的时间
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
6.3 内部类DelayedWorkQueue
底层使用数组维护最小堆,使用ReentrantLock和Condition实现线程等待
6.3.1 内部属性
// 默认长度
private static final int INITIAL_CAPACITY = 16;
// 底层是数组,默认长度是16,是最小堆
private RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
// 当前数据元素个数
private int size = 0;
// 队列头所在的leader线程。假如没有leader,在take时每个线程都会等待指定时间,大家一起被唤醒抢占资源,消耗性能
private Thread leader = null;
// 队列头的任务延时时间到了,或者新线程可能需要成为leader,用来唤醒等待线程
private final Condition available = lock.newCondition();
6.3.2 内部方法
/**
* 设置任务在堆中的位置
*/
private void setIndex(RunnableScheduledFuture<?> f, int idx) {
if (f instanceof ScheduledFutureTask)
((ScheduledFutureTask) f).heapIndex = idx;
}
/**
* 先把任务放到堆尾,然后向上调整位置
* @param k 任务最开始放到堆的位置
* @param key 任务
*/
private void siftUp(int k, RunnableScheduledFuture<?> key) {
while (k > 0) {
// 父节点位置
int parent = (k - 1) >>> 1;
// 获取父节点对应的任务
RunnableScheduledFuture<?> e = queue[parent];
// 如果新任务的延时时间比父任务的延时时间长,则退出
if (key.compareTo(e) >= 0)
break;
// 如果新任务的延时时间比父任务的延时时间短,则需要调整位置
// 父元素放到变到新位置
queue[k] = e;
setIndex(e, k);
// 计算新位置为父节点位置
k = parent;
}
// 得到的k是最终任务应该放到的位置
queue[k] = key;
// 设置任务在堆中的位置为k
setIndex(key, k);
}
/**
* 将任务放到堆k的位置,让后向下计算它在堆的实际位置
*/
private void siftDown(int k, RunnableScheduledFuture<?> key) {
// half是k最远能到达位置的下一个位置
int half = size >>> 1;
while (k < half) {
// 左子节点位置
int child = (k << 1) + 1;
// 下一个节点默认值是左节点
RunnableScheduledFuture<?> c = queue[child];
// 右子节点位置
int right = child + 1;
// 如果右子节点存在,并且 左子节点比右子节点大
if (right < size && c.compareTo(queue[right]) > 0)
// 下一个位置是右节点
c = queue[child = right];
// 如果当前值比子节点的最小值还要小,则当前位置k就是要key最后放的位置
if (key.compareTo(c) <= 0)
break;
// 当前位置跟比较小的结点交换位置
queue[k] = c;
setIndex(c, k);
// k变到较小的位置
k = child;
}
// 最终将key放到查找的到的位置
queue[k] = key;
setIndex(key, k);
}
/**
* 队列扩容,拿到锁的时候才能操作。
* 容量每次提升50%
*/
private void grow() {
int oldCapacity = queue.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%
if (newCapacity < 0) // overflow
newCapacity = Integer.MAX_VALUE;
queue = Arrays.copyOf(queue, newCapacity);
}
/**
* 移除某元素,向上或向下调整
*/
public boolean remove(Object x) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = indexOf(x);
if (i < 0)
return false;
setIndex(queue[i], -1);
int s = --size;
RunnableScheduledFuture<?> replacement = queue[s];
queue[s] = null;
if (s != i) {
// 把replacement放到i的位置,然后向下调整replacement位置
siftDown(i, replacement);
// 假如把replacement放到i位置后,直接就是最小的值,则他有可能比它现在的父节点小,需要向上调整
if (queue[i] == replacement)
siftUp(i, replacement);
}
return true;
} finally {
lock.unlock();
}
}
/**
* 不受容量限制,永远返回Integer.MAX_VALUE;
*/
public int remainingCapacity() {
return Integer.MAX_VALUE;
}
/**
* 入队
* @param x 任务
* @return 是否入队成功
*/
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>) x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = size;
// 数组达到容量最大值,则扩容
if (i >= queue.length)
grow();
// 元素数加一
size = i + 1;
// 如果之前数组是空的
if (i == 0) {
// 队首直接赋值
queue[0] = e;
// 设置该任务在堆中的位置是数组下标0
setIndex(e, 0);
} else {
// i是新节点入队的初始位置,任务入队,调整位置
siftUp(i, e);
}
// 如果堆顶是当前任务,
if (queue[0] == e) {
// 说明此时没有线程获取过堆顶任务
leader = null;
// 唤醒等待任务的线程
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
/**
* 弹出堆顶元素,堆尾放到堆顶,重新调整堆
*/
private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {
// 元素数量-1
int s = --size;
// 获取队尾元素
RunnableScheduledFuture<?> x = queue[s];
// 队尾置空
queue[s] = null;
// 把元素放到堆顶,然后向下计算该元素的位置
if (s != 0)
siftDown(0, x);
// 设置原来堆顶的元素在数组中的位置为-1.相当于移除队列
setIndex(f, -1);
// 返回堆顶元素
return f;
}
/**
* 非阻塞式获取数据,如果此时获取不到返回null
*/
public RunnableScheduledFuture<?> poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
RunnableScheduledFuture<?> first = queue[0];
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return finishPoll(first);
} finally {
lock.unlock();
}
}
/**
* 阻塞式获取任务,可中断
*/
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (; ; ) {
// 获取堆顶任务
RunnableScheduledFuture<?> first = queue[0];
// 堆顶为null则等待
if (first == null)
available.await();
else {
// 获取任务的剩余等待时间
long delay = first.getDelay(NANOSECONDS);
// 如果已经达到执行时间,则堆顶出队
if (delay <= 0)
return finishPoll(first);
// 等待的时候不保持引用,被唤醒后要重新从堆顶拿
first = null;
// leader被占用的时候,说明还有其它线程也在等待,设置当前线程无期限等待。
if (leader != null)
available.await();
else {
// 如果leader为null
// 记录当前线程
Thread thisThread = Thread.currentThread();
// leader为当前线程
leader = thisThread;
try {
// 等待delay时间
available.awaitNanos(delay);
} finally {
// 如果leader是当前线程
if (leader == thisThread)
// leader置为null
leader = null;
}
}
}
}
} finally {
// 如果堆中还有元素,并且此时没有线程占据leader,则唤醒下一个线程
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
/**
* 非阻塞式,等待指定时间
*/
public RunnableScheduledFuture<?> poll(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (; ; ) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null) {
if (nanos <= 0)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
if (nanos <= 0)
return null;
first = null; // don't retain ref while waiting
if (nanos < delay || leader != null)
nanos = available.awaitNanos(nanos);
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
long timeLeft = available.awaitNanos(delay);
nanos -= delay - timeLeft;
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
/**
* 堆顶过期则弹出
*/
private RunnableScheduledFuture<?> peekExpired() {
RunnableScheduledFuture<?> first = queue[0];
// 队首是null或者当前没有到期的任务,则返回null,否则返回堆顶任务
return (first == null || first.getDelay(NANOSECONDS) > 0) ?
null : first;
}
/**
* 将所有到期的任务从堆中移到新的集合中,并返回移动的数量
*/
public int drainTo(Collection<? super Runnable> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
RunnableScheduledFuture<?> first;
int n = 0;
while ((first = peekExpired()) != null) {
c.add(first); // In this order, in case add() throws.
finishPoll(first);
++n;
}
return n;
} finally {
lock.unlock();
}
}
/**
* 将所有到期的任务从堆中移到新的集合中,并返回移动的数量,最多可以移动maxElements个
*/
public int drainTo(Collection<? super Runnable> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
RunnableScheduledFuture<?> first;
int n = 0;
while (n < maxElements && (first = peekExpired()) != null) {
c.add(first); // In this order, in case add() throws.
finishPoll(first);
++n;
}
return n;
} finally {
lock.unlock();
}
}
/**
* 数组的副本
*/
public Iterator<Runnable> iterator() {
return new Itr(Arrays.copyOf(queue, size));
}
/**
* 内部类处理内部数组副本的迭代器
*/
private class Itr implements Iterator<Runnable> {
final RunnableScheduledFuture<?>[] array;
int cursor = 0; // index of next element to return
int lastRet = -1; // index of last element, or -1 if no such
Itr(RunnableScheduledFuture<?>[] array) {
this.array = array;
}
public boolean hasNext() {
return cursor < array.length;
}
public Runnable next() {
if (cursor >= array.length)
throw new NoSuchElementException();
lastRet = cursor;
return array[cursor++];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
DelayedWorkQueue.this.remove(array[lastRet]);
lastRet = -1;
}
}