schedule和scheduleAtFixedRate区别:
两种方法都是在延迟一段时间后,开始周期性执行任务,而又有一定细微的差别。可以看到,schedule在调用sched方法时,传入了负的period。
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); // 传入负数 <0
}
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); // 传入正数 >0
}
Timer对象内部有一个TimerThread线程,对象创建后,此线程就开始运行,所有任务都是有此线程调度,而不是每个任务创建一个线程执行。其中mainLoop就是任务调度的核心部分。若period不为0,说明是周期性执行的任务,根据period的正负决定是根据本次实际运行时间还是理论运行时间计算下次执行时间。若每次任务的执行时间都没有被延迟,二者就没有什么区别。当任务某次执行发生了延迟时,二者的区别就真正体现出来了。
private void mainLoop() {
while (true) { // 无限循环,从队列(最小堆)中调度任务执行
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 等待任务队列中有任务加入(sche中notify)
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break;
long currentTime, executionTime;
task = queue.getMin(); // 从堆顶获取任务,此任务nextExecutionTime最小
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // 当前任务被取消,从堆中删除
}
currentTime = System.currentTimeMillis(); // 当前时间,是实际运行时间
executionTime = task.nextExecutionTime; // 当前任务的将要运行的时间,是理论运行时间
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // 非周期调度任务,调度后,将其状态设置为EXECUTED(见TimerTask)
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // 周期调度任务,本次执行后,计算下次执行时间
// 如果period<0(fixed-delay),则根据实际运行时间计算下次运行时间,若某次任务被耽搁延迟执行,
// 则此后任务执行时间依次顺延。
// 如果period>0(fixed-rate),则根据理论运行时间计算下次运行时间,尽量不影响下次理论运行时间
// 比如,如果某个任务立即周期运行,间隔10s,则理论每次运行时间为:0s、10s、20s、30s...
// 当第二次运行时间由于某种原因(比如CPU调度延迟了)导致其实际运行时间是第12s,则:
// fixed-delay运行时间:0s、12s、22s、32s... 尽量保证间隔时间为10s
// fixed-rate运行时间:0s、12s、20s、30s... 尽量保证运行时间为理论运行时间
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired)
queue.wait(executionTime - currentTime);
}
if (taskFired) // 在TimerThread线程中直接调用任务的run方法
task.run();
} catch(InterruptedException e) {
}
}
}