jdk定时任务的原理

先来看下timer的调用方式,简单的定时打印java

public static void main(String[] args){
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		}, 5000, 5000);
	}

运行结果:node

Timer-0
Timer-0
Timer-0
Timer-0
Timer-0多线程

而后我好奇它是如何实现定时调用的,打开TimerTask类的源码,发现它是一个对runable的封装,添加了一些状态和同步锁对象以及下次执行的时间,它封装了如下状态ide

 /**
     * This task has not yet been scheduled.任务尚未被调用
     */
    static final int VIRGIN = 0;oop

    /**
     * This task is scheduled for execution.  If it is a non-repeating task,
     * it has not yet been executed. 任务被调度去执行,若是是一个不循环执行的任务,表示它尚未被执行
     */
    static final int SCHEDULED   = 1;ui

    /**
     * This non-repeating task has already executed (or is currently
     * executing) and has not been cancelled.不重复执行任务已经执行或者正在执行,不能被取消
     */
    static final int EXECUTED    = 2;this

    /**
     * This task has been cancelled (with a call to TimerTask.cancel).任务已经被取消,(调用 timerTask 的cancel 方法
     */
    static final int CANCELLED   = 3;线程

 /**code

    * 下次执行的时间,System.currentTimeMillis 格式
     * Next execution time for this task in the format returned by
     * System.currentTimeMillis, assuming this task is scheduled for execution.
     * For repeating tasks, this field is updated prior to each task execution.
     */
    long nextExecutionTime;orm

    /**

    * 执行间隔
     * Period in milliseconds for repeating tasks.  A positive value indicates
     * fixed-rate execution.  A negative value indicates fixed-delay execution.
     * A value of 0 indicates a non-repeating task.
     */
    long period = 0;

     

   以上能够看出timer+timerTask的方式不适合使用天然时间的任务。

 

    Timer.class

   timer主要有两个关键的成员变量final修饰

    //内部类实现,timerTask的队列,封装实现了,最大128个任务

    private final TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.,内部类,一个线程的封装实现
     */
    private final TimerThread thread = new TimerThread(queue);

 

   先简单介绍下TaskQueue.class,当作员变量

/**

    保存定时任务队列,能够看到最大一个timer最多能够提交128个任务
     * Priority queue represented as a balanced binary heap: the two children
     * of queue[n] are queue[2*n] and queue[2*n+1].  The priority queue is
     * ordered on the nextExecutionTime field: The TimerTask with the lowest
     * nextExecutionTime is in queue[1] (assuming the queue is nonempty).  For
     * each node n in the heap, and each descendant of n, d,
     * n.nextExecutionTime <= d.nextExecutionTime.
     */
    private TimerTask[] queue = new TimerTask[128];

    /**
     * The number of tasks in the priority queue.  (The tasks are stored in
     * queue[1] up to queue[size]).
     */
    private int size = 0;

  TaskQueue提供了一系列方法是对该队列即将执行的任务的操做,获取最近要执行的人,修改任务的下次执行时间

   主要是有两个私有方法fixUp(),fixDown();用来更新队列的顺序。

 

    再来看下TimerThread.class,主要看下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)实现线程一直在运行,而不是任务到时间了另起一个线程,因此timer只有一个线程
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        //new timer()的时候线程已经启动了的,在这里等带任务的提交,为何是用while呢?
                        queue.wait();
                    if (queue.isEmpty())//队列空直接退出,在中止timer的状况下
                        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的定时实际上是使用一个线程while(true)和,wait(),notify()配合实现的。代码有注释很少解释了。

 

总结:

jdk timer + timertask的方式实现的定时任务,因为实现方式的局限性,致使不能支持更多的特性,多线程,天然时间等。 

相关文章
相关标签/搜索