@Yano
2018-07-02T15:39:57.000000Z
字数 4753
阅读 1382
Java
本文分析的是 JDK 7
public static void main(String[] args) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(new Date(this.scheduledExecutionTime()));
}
}, 500L, 1000L);
}
输出:
Mon Jul 02 14:34:20 CST 2018
Mon Jul 02 14:34:21 CST 2018
Mon Jul 02 14:34:22 CST 2018
Mon Jul 02 14:34:23 CST 2018
Mon Jul 02 14:34:24 CST 2018
Mon Jul 02 14:34:25 CST 2018
主要能够指定定时任务的初始延迟、间隔执行时间。
首先画了一张示意图,能够说明 Timer 的基本原理。
大体上有 4 个类:
- Timer:定时器主类
- TimerTask:实现了 Runnable 的抽象类(run 仍为抽象方法,需要用户实现),定义了与 Timer 特有的任务状态(下一次执行时间 nextExecutionTime 和 执行时间间隔 period)
- TimerThread:由 Timer 启动,里面有个无限循环的 mainLoop 方法,用来取出 TaskQueue 中最近需要执行的任务,判断是否需要执行。
- TaskQueue:由 TimerTask 组成的小顶堆,其排序是根据 TimerTask 的 nextExecutionTime。
/**
* The state of this task, chosen from the constants below.
*/
int state = VIRGIN;
/**
* This task has not yet been scheduled.
*/
static final int VIRGIN = 0;
/**
* This task is scheduled for execution. If it is a non-repeating task,
* it has not yet been executed.
*/
static final int SCHEDULED = 1;
/**
* This non-repeating task has already executed (or is currently
* executing) and has not been cancelled.
*/
static final int EXECUTED = 2;
/**
* This task has been cancelled (with a call to TimerTask.cancel).
*/
static final int CANCELLED = 3;
函数的定义如下:
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
注意其中最大的区别,在于 schedule 调用 sched 函数时,将传入的 period 取反了。如果某次执行任务的开始时间延后了,那么此后的每次任务都会延迟。
If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well. In the long run, the frequency of execution will generally be slightly lower than the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate). As a consequence of the above, if the scheduled first time is in the past, it is scheduled for immediate execution.
而 scheduleAtFixedRate 则并不会这样。
If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up." In the long run, the frequency of execution will be exactly the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate).
其加入后的核心函数是 sched。参数 period
- >0:重复任务的时间间隔(scheduleAtFixedRate)
- =0:仅执行一次
- <0:重复任务的时间间隔(schedule)
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
/**
* The main timer loop. (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
整体流程是:获取 queue 的锁,若 queue 不为空,则拿出第一个任务,获取任务的锁,若任务的状态正常且到执行时间,则执行任务;否则将其 wait 对应的时间。
其最核心的代码:
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
Timer 是 JDK 自带的执行定时任务的工具类,用户能够指定延迟、任务执行的时间间隔。当 new 一个 Timer 时,会自动启动一个 TimerTask 线程,这个线程无限循环一个小顶堆的任务队列,每次取出最近需要执行的任务,如果符合条件则对该任务(小顶堆)做相应处理。