#对java Timer的分析
为什么要分析Timer的源码进行分析?
希望对Timer类的了解,加深对任务调度的理解。
###Timer源码的分析
public void schedule(TimerTask task, long delay);//经过特定的时间执行
public void schedule(TimerTask task, Date time);//在特定的时间运行
public void schedule(TimerTask task, long delay, long period);//每经过特定的时间再次运行
public void schedule(TimerTask task, Date firstTime, long period);
public void scheduleAtFixedRate(TimerTask task, long delay, long period);//也是经过特定的时间进行运行。
这是Timer的基本用法。
上面的方法都会调用
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();
}
}
我们可以看到为了线程安全,首先对queue进行了锁定,然后设置了task下一次的执行时间,以及周期。
那么queue添加task会发生什么事情呢?TaskQueue是一个以执行时间排序的二叉树,距离执行时间最短的越在上面。Timer在初始化的时候会初始化一个TimerThread,并且start它。在run()里面会进行调用mainLoop(),而mainLoop()如下面代码所示:
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中剔除掉,如果不是则需要重新将task添加到queue中,至于为什么需要判断task.period小于0,则是为了区别 public void schedule(TimerTask task, long delay)以及 public void scheduleAtFixedRate(TimerTask task, long delay, long period);这两种任务,AtFixedReate意味着每次都是在特定的时间点执行,与上一次的执行时间无关。而schedule类型的任务下一次的执行时间为本次任务的执行时间点+delay,意味着本次任务的执行与上次的执行有关
注意点:
- 在某一个Task抛出异常后,那么整个Timer就会停止。
- 单个任务会影响别的任务执行