读Timer源码理解jdk定时任务

在java中实现定时任务的方式很多,比如quartz框架的任务调度,jdk的ScheduledExecutorService任务调度,spring中集成的调度以及netty中集成的调度,今天我要介绍的是jdk原生的比较原始的timer定时任务的实现方式,废话少说直接上代码。

首先介绍下Timer这个类所包含的关键属性。

  //TaskQueue是一个任务队列,其内部定义一个TimerTask数组用来维护定时任务
 private TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.
     */
  //定时器中用来执行任务的线程
 private TimerThread thread = new TimerThread(queue);
再来介绍下这俩个属性的类型

    TaskQueue

//TaskQueue内部保存TimeTask的数组
private TimerTask[] queue = new TimerTask[128];
/**往TaskQueue队列中添加任务时,会先判断队列是否已满,如果满了,会进行俩倍扩容,然后将任务添加至数组末端,再
*将数组根据TimeTask的nextExecutionTime进行排序,把最小的排到数组首部,即最先执行的任务排到队列首部,线程每次都是取
*队首的任务对象
**/
 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);
    }
 //这里贴上数组排序函数
 private void fixUp(int k) {
        while (k > 1) {
            int j = k >> 1;
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break;
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

    TimerThread贴上主要逻辑代码

    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // 如果队列为空并且Timer状态有效,则释放cpu等待
                    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;
                       //如果当前时间大于等于task的执行时间,则说明任务可以执行了,所以如果一个处理时间比较长的任务    //很可能覆盖掉其他任务的执行时间,导致任务不能按时执行,出现滞后情况。
                       if (taskFired = (executionTime<=currentTime)) {
        //如果该任务不是重复执行的任务,只要求执行一次,则执行完后从队列中移除
        if (task.period == 0) { // Non-repeating, remove
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            }
      /**
        *是重复执行的任务则重新设置间隔时间
      **/ else {
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
     //任务没触发,即还没到任务执行的时间则等待到任务执行时间

    if (!taskFired) 
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }
}
介绍完这俩个主要的对象,再来介绍下TimeTask这个对象。

public abstract class TimerTask implements Runnable {
 
    final Object lock = new Object();

    int state = VIRGIN;

    static final int VIRGIN = 0;

    static final int SCHEDULED   = 1;

    static final int EXECUTED    = 2;

    static final int CANCELLED   = 3;

    long nextExecutionTime;

    long period = 0;
   protected TimerTask() {
    }
    public abstract void run();

    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);
        }
    }
}
可以看出TimeTask是一个实现了Runnable的任务抽象类,我们自定义的任务类必须继承TimeTask类并根据我们自己的逻辑实现其中的run方法。

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.");

        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是通过一个线程来执行任务队列的,这引发一个问题,如果一个处理时间较长的任务很可能会覆盖掉其他的调度任务。另外,如果一个任务在执行期间抛出异常就会终止定时器的执行,从而其他的任务也不能执行,所以在比较复杂的环境下,一般不推荐使用jdk Timer来支持任务调度。

由于个人能力有限,以上只是本人比较浅显的理解,有什么不当的地方,还望大家指正。


你可能感兴趣的:(java技术点)