Java定时任务 - Timer 原理

Java定时任务 - Timer 原理

概要

Jdk库自带有两种实现定时任务的技术。一种是通过Timer,另外一个是通过ScheduledThreadPoolExecutor。下面为大家分析Timer实现的原理。

一、Timer

1、Timer使用

public class TimerTest extends TimerTask {

    @Override
    public void run() {
        System.out.println("test1 --------- " + Thread.currentThread());
    }

    static class TimerDemo {
        public static void main(String[] args) {
            Timer timer = new Timer();
            // 指定三秒后执行TimerTest中的run方法,每间隔1秒执行一次
            timer.schedule(new TimerTest(), 3000, 1000);
        }
    }
}

2、源码分析

看到上面的实践后是不是觉得很简单?接着我们分析下上面的代码。

首先要注意TimerTest这个类继承了TimerTask

TimerTask
public abstract class TimerTask implements Runnable {
    /**
     * This object is used to control access to the TimerTask internals.
     */
    final Object lock = new Object();

    /**
     * The state of this task, chosen from the constants below.
     */
    int state = VIRGIN;

    /**
     * This task has not yet been scheduled.
     */
    static final int VIRGIN = 0;

    /**
     * This task is scheduled for execution.  If it is a non-repeating task,
     * it has not yet been executed.
     */
    static final int SCHEDULED   = 1;

    /**
     * This non-repeating task has already executed (or is currently
     * executing) and has not been cancelled.
     */
    static final int EXECUTED    = 2;

    /**
     * This task has been cancelled (with a call to TimerTask.cancel).
     */
    static final int CANCELLED   = 3;

    /**
     * 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;

    /**
     * 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;

    /**
     * Creates a new timer task.
     */
    protected TimerTask() {
    }

    /**
     * The action to be performed by this timer task.
     */
    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);
        }
    }
}

去掉注释后,这个类的代码也就几十行。你可以看到TimerTask实现了Runnable接口,有一个抽象的 run() 方法,我们的TimerTest继承并实现了 run() 方法。以及这个类有几个成员变量:

lock:用来线程加锁

state:表示任务的状态

nextExecutionTime:下一次执行任务的时间

period:执行任务的周期

而它只有两个方法 :

cancel() :取消任务,改变任务的state

scheduledExecutionTime():设置下次要执行的时间

Timer

我们在来分析下Timer类,先附上一张思维导图,方便记忆理解

Java定时任务 - Timer 原理

下面简单列举Timer关键代码,具体代码可以自己去查看

public class Timer {
    /** 用来存放要执行的任务,通过最小堆维护的队列 **/
    private final TaskQueue queue = new TaskQueue();
    
    /** 负责执行任务的线程 **/
    private final TimerThread thread = new TimerThread(queue);
    
    /** 构造方法 设置了线程名称 是否为守护进程 最后启动了这个线程 **/
    public Timer(String name, boolean isDaemon) {
        thread.setName(name);
        thread.setDaemon(isDaemon);
        thread.start();
    }
}

在来看看TimerThread这个类

TimerThread
class TimerThread extends Thread {
    
    boolean newTasksMayBeScheduled = true;
    
    private TaskQueue queue;
    
    public void run() {
        try {
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }
    
    /** 关键代码 执行定时任务 **/
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // 如果队列为空,且newTasksMayBeScheduled为true 则线程进入等待状态
                    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);
                            }
                        }
                    }
                    // Task hasn't yet fired; wait
                    if (!taskFired) 
                        queue.wait(executionTime - currentTime);
                }
                //如果任务的执行时间到了,就执行这个任务
                if (taskFired)  
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }
}

我们已经大概知道TimerThread是怎么循环执行定时任务的了,接下来看看Timer的schedule()以及sched()方法

schedule & sched
public void schedule(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);
}


private void sched(TimerTask task, long time, long period) {
    if (time < 0)
        throw new IllegalArgumentException("Illegal execution time.");

    // 获取执行周期 period 的绝对值,如果大于 二分之一的 Long.MAX_VALUE, 则将 period 除以二
    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);
        // 如果任务加入队列后排在堆顶,则直接唤醒线程执行任务 (还记得之前TimerThread中让线程等待吗?)
        if (queue.getMin() == task)
            queue.notify();
    }
}

3、总结

在我们创建Timer时候就会初始化并启动一条线程,用来执行任务,并初始化一个优先队列,使用的是最小堆数据结构,将最早执行的任务放在堆顶。

当我们调用 schedule() 方法时候,就是将任务放进优先队列中。TimerThread循环判断是否已经达到执行的时间,如果到了先计算出下次执行的时间以及调整堆,最后在执行任务,如果没到则线程进入等待。

上一篇:NetBIOS计算机名称命名限制


下一篇:c++11 利用chrono 计算代码执行时间