Java原生api Timer类就可以实现简单的定时任务。下面将简单介绍一下Timer。
一、使用 Timer 实现定时任务
具体代码如下。
可以看到我们主要是分三步进行的
1、new Timer() 创建定时器
2、new TimerTask() 创建任务。这里是通过继承TimerTask类实现的。
3、使用定时器调度。这里我们使用的是Timer类中的schedule(TimerTask task, long delay, long period) 方法,里面有三个参数,
第一个参数表示任务,第二个参数表示延迟的时间,第三个参数表示任务执行的间隔(也就是每隔多少秒执行一次任务)。
public class TimerTest { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTest().new MyTask(), 1000, 5000); } /** * 任务 * @author LKB * */ class MyTask extends TimerTask{ @Override public void run() { // TODO Auto-generated method stub System.out.println("current time is " + new Date()); } } }
二、Timer 实现定时任务的原理
Timer 的实现需要四个类:Timer TimerThread TaskQueue TimerTask。
2.1 TimerTask
其中 TimerTask 表示任务,它是个抽象类TimeTask 抽象类 继承Runnable。可以看到每个Task都是一个runnable对象。
TimeTask中全局变量有
lock 锁
state VIRGIN: 任务未被调度
SCHEDULED: 任务被调度但是还未执行
EXECUTED: 任务已经被执行但是未被取消
CANCELLED:任务被取消
nextExecutionTime:任务下一次被执行的时间
period:重复任务之间的间隔
内部方法有
//取消调度 public boolean cancel() { synchronized(lock) { boolean result = (state == SCHEDULED); state = CANCELLED; return result; } } //计算下次执行时间 public long scheduledExecutionTime() { synchronized(lock) { return (period < 0 ? nextExecutionTime + period : nextExecutionTime - period); } }
2.2 TaskQueue
TaskQueue 是一个优先队列,它是通过一个数组实现的。
全局变量有:
private TimerTask[] queue = new TimerTask[128]; 一个数组,数组元素类型为TimerTask。这个数组用来实现优先队列。
private int size = 0; //优先队列中的任务个数 需要注意的是任务在优先队列中的存储是queue[1]-queue[size]
内部的方法有挺多的,最基本的我认为是下面几个:
/** * Adds a new task to the priority queue. */ void add(TimerTask task) { // Grow backing store if necessary if (size + 1 == queue.length) queue = Arrays.copyOf(queue, 2*queue.length); queue[++size] = task; fixUp(size); } /** * Return the "head task" of the priority queue. (The head task is an * task with the lowest nextExecutionTime.) */ TimerTask getMin() { return queue[1]; } /** * Remove the head task from the priority queue. */ void removeMin() { queue[1] = queue[size]; queue[size--] = null; // Drop extra reference to prevent memory leak fixDown(1); }
add 是往队列中添加任务,removeMin 是移除队头元素。因为 TaskQueue 是通过优先队列实现的,所以的删除或者添加一个元素的复杂度为O(lgN)。
2.3 TimerThread
TimerThread 继承Thread 类,Timer 定时器正是通过这个线程实现任务调度的。
全局变量有
boolean newTasksMayBeScheduled = true; //如果该标志位false 则表示对 Timer 对象没有任何有效的引用,没有任何的任务需要做了,线程可以优雅地终止了
private TaskQueue queue; //任务队列
重写的 run 方法如下
public void run() { try { mainLoop(); } finally { // Someone killed this Thread, behave as if Timer cancelled synchronized(queue) { newTasksMayBeScheduled = false; queue.clear(); // Eliminate obsolete references } } } /** * The main timer loop. (See class comment.) */ 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) { } } }
可以看到TimerThread 运行的方式很简单,在一个while(true)循环中,首先判断任务队列中是否有任务,没有任务则一直等待;
如果有任务,取出该任务(运行时间离当前时间最近的任务),判断任务时间,任务时间到了,则调用task.run()运行任务。
2.4 Timer
Timer 是一个调度器,该调度器内部只有一个调度线程,并且只有一个任务队列。
全局变量有:
private final TaskQueue queue = new TaskQueue(); //任务队列
private final TimerThread thread = new TimerThread(queue); //调度线程
private final Object threadReaper = new Object() {
protected void finalize() throws Throwable {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.notify(); // In case queue is empty.
}
}
}; //该对象可以使得当没有任何有效引用指向Timer对象时,定时器被优雅回收
/**
* This ID is used to generate thread names.
*/
private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
private static int serialNumber() {
return nextSerialNumber.getAndIncrement();
}
最基本的构造器如下。可以看到,当我们new一个 Timer 对象出来时,Timer 中对应的调度线程就被启动了。
public Timer(String name) { thread.setName(name); thread.start(); }
最基本的调度方法如下。可以看出它其实就是对任务队列的一个操作。
首先将当前任务配置好(计算好下次执行时间,间隔与状态),然后加入任务队列,如果刚好是队列的队头,再将队列唤醒,让队列直接处理。
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(); } }
综上,我们可以任务定时器的工作原理如下:
Timer 内部拥有一个 TimerThread 和 一个TaskQueue,当我们new Timer() 时,我们就拥有了这两个对象,并启动了TimerThread。
当我们这样调动 timer.schedule(new TimerTest().new MyTask(), 1000, 5000); schedule方法时我们就将对应的任务加到TaskQueue中。
TimeThread 负责调度TaskQueue,TaskQueue 是一个优先队列,里面的元素全是TimerTask 对象。