标签:
Java中ScheduleThreadPoolExecutor主要用于执行延迟任务或者按照一定的频率执行任务。其中scheduleAtFixedRate函数是按照一定频率执行任务,scheduleWithFixedDelay可以根据延迟一定时间再执行任务。本文将参考ScheduleThreadPoolExecutor的源码来剖析其为什么能够支持延迟并按照固定频率执行任务。
ScheduleThreadPoolExecutor之所以能够延迟并按照一定频率执行任务主要依靠其内部封装的两个内部类,ScheduledFutureTask和DelayedWorkQueue。其中ScheduledFutureTask继承了FutureTask类,因此其可以封装继承了Runable或Callable接口的任务。而DelayedWorkQueue则为一个延迟队列,其利用最小堆实现,需要首先执行的任务在堆顶,这样每次执行任务时只需要获取堆顶的任务即可。
ScheduledFutureTask继承了FutureTask,因此其能够被ScheduledExecutorService执行。下面看一下ScheduledFutureTask的一些重要属性:
对于ScheduledFutureTask的方法,最主要的是compareTo和getDelay和setNextRunTime方法。
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), NANOSECONDS);
}
该方法主要是用来获得任务需要执行时的延迟时间,其在DelayedWorkQueue中的offer函数中有重要的运用。
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;
}
compareTo方法主要用于DelayQueue中最小堆的排序,其首先根据任务执行时间来判断,如果任务执行时间相同则按照队列的FIFO规则进行判断。
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}
setNextRunTIme()方法主要是为需要重复执行的任务设置下次执行的时间,当period > 0时表示任务是按照一定速率执行的,只需要将本次执行时间加上间隔时间即可。当period < 0时表示任务是延期执行的,需要调用triggerTime来获得下次执行时间。下面是triggerTime函数的实现:
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
/**
* Constrains the values of all delays in the queue to be within
* Long.MAX_VALUE of each other, to avoid overflow in compareTo.
* This may occur if a task is eligible to be dequeued, but has
* not yet been, while some other task is added with a delay of
* Long.MAX_VALUE.
*/
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
long headDelay = head.getDelay(NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
由上面的代码可知,对于延迟执行的任务,执行时间是当前时间加上延迟时间。而为了防止在conpareTo进行比较时数值过大,延迟时间又是根据队列中下一个要执行的任务的执行时间来获得。下一篇讲介绍DelayQueue的详细实现。
ScheduleThreadPoolExecutor源码分析
标签:
原文地址:http://www.cnblogs.com/lanxing/p/5680497.html