您的位置:首页 > 其它

Timer的源码分析

2015-12-19 21:54 344 查看

对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,意味着本次任务的执行与上次的执行有关

注意点:

  

1. 在某一个Task抛出异常后,那么整个Timer就会停止。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: