原文链接xingzhi.info/?p=117,转载请注明出处
jdk提供了两种定时任务的选择方案,分别是Timer,和ScheduledExecutorService
Timer的用法大体如下:定义Task,在run方法中写明,这个任务是干什么。
public class XXXXXTask extends TimerTask { @Override public void run() { //该干嘛干嘛 } }
然后用Timer来调用他, 下面是在一分钟整点的时候执行。
public class YYYYYScheduler { private Timer timer = new Timer(); public void start() { Date date = new Date(); long curTime = date.getTime(); long period = 60000;// 整1分钟 long delay = (period - (curTime%period)); // 整1分钟 timer.scheduleAtFixedRate(new XXXXXTask(), delay, period); } }
实现原理,下面所说是从jdk的源码中抽出来的,版本是1.6。
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); } 上面是timer.scheduleAtFixedRate源代码 他会最终会调用sched(TimerTask task, long time, long period)这个方法,里面的源码如下: 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进行加锁, 线程加锁的这个queue是task队列
/**
* The timer task queue. This data structure is shared with the timer
* thread. The timer produces tasks, via its various schedule calls,
* and the timer thread consumes, executing timer tasks as appropriate,
* and removing them from the queue when they're obsolete.
*/
private TaskQueue queue = new TaskQueue();
下面的代码,可以看出锁住某个task之后,会对他设定执行的时间,这个时间就是传入的那个开始的延时
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; }
就是这个task.nextExecutionTime = time;。
在Timer的里面有一个run方法,里面有一个mainloop,如下所示:
public void run() { try { mainLoop(); } finally { // Someone killed this Thread, behave as if Timer cancelled synchronized(queue) { newTasksMayBeScheduled = false; queue.clear(); // Eliminate obsolete references } } }
打开这个mainLoop方法就是里面的执行过程,这个Timer运转起来的核心
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) { } } }
从下面这几句可以看出,Timer的执行是用的绝对时间:
//1. 获取系统当前时间 currentTime = System.currentTimeMillis(); //2.从task里面去除下次执行时间 executionTime = task.nextExecutionTime; //3.看下次执行时间是不是比当前时间小,接下来再判断是不是重复执行, 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让定时任务等待,用的是wait方法。
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
总结:
Timer就是把TimerTask方法任务队列,获取系统当前时间,来判断task是否执行,不到时间的话,就一直wait。
这种方式的话,系统时间调整,Timer就被打乱了。