Timer只是管理着一个TimerTask任务队列(queue)而TimerTask的真正执行是由TimerThread来执行的,TimerThread继承了Thread,它的主要工做就是不断从优先级队列queue中取出任务检查是否到了能够执行的时间,若是能够执行就调用TimerTask的run方法。java
很是值得注意的是任务队列(queue)中的TimerTask虽然实现了Runnable接口,可是在TimerThread中并无启动新的线程来执行它,只有Thread的start方法才能启动新的线程。因此若是TimerTask中的run方法是死循环,那么可能Timer的任务队列中的TimerTask永远得不到执行的机会。仍是先看一下TimerThread的代码吧。oop
import java.util.TimerTask; public class TimerThread extends Thread { boolean newTasksMayBeScheduled = true; /** * 任务队列的引用 */ private TaskQueue queue; TimerThread(TaskQueue queue) { this.queue = queue; } public void run() { try { mainLoop(); } finally { // 任务结束,例如Timer 执行了cancel synchronized(queue) { newTasksMayBeScheduled = false; queue.clear(); //清除废弃对象的引用 } } } /** * */ private void mainLoop() { while (true) { try { TimerTask task; boolean taskFired; synchronized(queue) { // 若是暂时没有任务,而且TimerThread线程没有被取消就等待 while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait(); if (queue.isEmpty()) break; // 若是任务队列为空而且TimerThread已经被取消了,直接结束 long currentTime, executionTime; //咱们已经知道queue队列是一个按下一次执行时间排序的优先队列 //因此,这里是获取到执行时间最近的TimerTask task = queue.getMin(); //主要防止TimerTask的state域被其余线程修改,而形成数据不一致 synchronized(task.lock) { if (task.state == TimerTask.CANCELLED) { queue.removeMin(); continue; } currentTime = System.currentTimeMillis(); executionTime = task.nextExecutionTime;//下一次执行的时间 //若是下一次执行时间小于等于当前时间说明能够执行了 if (taskFired = (executionTime<=currentTime)) { // period等于0说明不是周期执行的,就直接移除并把TimerTask的状态修改成执行过了 if (task.period == 0) { queue.removeMin(); task.state = TimerTask.EXECUTED; } // 这里比较巧妙,从新计算下一次的执行时间,咱们知道period<0是按固定的时间间隔执行 //看下面的代码当period<0的时候下一次的执行时间(注意减的是负数) //nextExecutionTime = currentTime - period,因此每一次计划执行完以后的period再执行 //period>0是按固定的频率执行,显然在executionTime<=currentTime条件下同 //executionTime + task.period计算一下次执行时间就尽可能保证了在每两次计划执行期间的period //尽可能相等,由于executionTime每一次都是计算出来的是固定的,因此它能保证频率固定 else { queue.rescheduleMin( task.period<0 ? currentTime - task.period : executionTime + task.period); } } } // 任务尚未被移除,说明最小的执行时间都尚未到,直接等待 if (!taskFired) queue.wait(executionTime - currentTime); } // 若是任务(TimerTask)已经从队列中移除,说明已经到了执行时间,能够执行 if (taskFired) task.run();//只是调用的run方法没有启用新的线程 } catch(InterruptedException e) { } } } }
在mainLoop中有这样一句代码:this
queue.rescheduleMin(task.period<0 ? currentTime - task.period : executionTime + task.period);
TimerTask中以一个nextExecutionTime字段是表示下一次的执行时间的,上面的语句就是从新设置队列优先级最高(堆顶)元素的下一次执行时间的语句。当period<0的时候currentTime-period就是加上period绝对值。currentTime是当前的时间是变化的,period是固定的,因此固定延迟(fixed-delay)就好理解了,就是无论TimerTask执行了多久,从新安排执行计划的时候却是按当前时间向后延迟period这么长的时间。线程
当period>0的时候表示固定频率(fixed-rate)执行,executionTime就是TimerTask的 nextExecutionTime , 当period>0的时候 nextExecutionTime 每一次都是有上一次的 nextExecutionTime +period计算来的,它两次执行区间可能就不须要等period这么长时间了。例如:没有延迟当即开始执行,每一次执行2分钟,period为3。使用固定延迟就是0分钟执行第一次,执行了2分钟而后下一次执行时间就是2+3=5,而后在执行2分钟第三次执行时间就是7+3=10分钟的时候。而使用固定频率的方式就是第一次执行是在0分钟,下一次执行时间是在0+3=3分钟的时候执行,而后他就会在第3分钟执行,第二次是在3+3=6分钟的时候执行,它和执行的时间基本没有关系,就是一个等差数列。code
由于Timer主要对外提供的接口就是schedule方法,因此Timer自己没有太多的内容,可是它有一个很是好技巧,就是他对外提供的不一样参数的固定频率执行,固定延迟执行不少接口,可是它的内部实现只是巧妙的经过参数的变换把它抽象都了一个方法中。有时候以为别人的类写的特别好用从这里就能够看出一点,不要让使用接口的人来控制变化来作不一样的实现,不多有人愿意记住,而是经过直观的方法名来对外提供接口,咱们内部实现来控制变化。看Timer提供的的各类schedule接口基本都是经过period等参数变化实现的。而咱们看到的就是直观的schedule方法。
对象