就以闹钟的例子开头吧(后续小节皆以闹钟为例,所有源代码只列关键部分)。

public class ScheduleDemo {

    public static void main(String[] args) throws InterruptedException {
        long delay = 1000;  // 一秒后开始执行
        long period = 2000; // 执行间隔
        Timer timer = new Timer();
        AlarmTask alarm = new AlarmTask("闹钟1");
        log.info("["+Thread.currentThread().getName()+"]开启闹钟调度!");
        timer.schedule(alarm,delay,period);
    }

    /**
     *     模拟闹钟
     */
    static class AlarmTask extends TimerTask{
        String name ;
        public AlarmTask(String name){
            this.name=name;
        }
        @Override
        public void run() {
            log.info("["+Thread.currentThread().getName()+"]"+name+":嘀。。。");
            Thread.sleep(1000); //模拟闹钟执行时间,省略异常。。。
        }
    }
}

一秒以后闹钟每隔两秒执行一次。

[main]    开启闹钟调度!
[Timer-0] 闹钟1:嘀。。。
[Timer-0] 闹钟1:嘀。。。
[Timer-0] 闹钟1:嘀。。。

从打印结果可以看到,闹钟调度与执行并非一线程。

下面是Timer时序图,可以了解Timer的大概流程。Java定时任务Timer调度器【一】 源码分析(图文详解版)-LMLPHP

下面开始分析Timer源码。

public class Timer {

    private final TaskQueue queue = new TaskQueue();

    private final TimerThread thread = new TimerThread(queue);

    public Timer() {
        this("Timer-" + serialNumber());
    }

    public Timer(String name) {
        thread.setName(name);
        thread.start();
    }

可以看到,Timer中维护了一个内部线程与队列,且在实例化Timer的同时,就已初始化好了。在初始化Timer时,内部线程TimerThread开始启动,下面是TimerThread的执行过程。

class TimerThread extends Thread {

    public void run() {
        mainLoop();
    }

    private void mainLoop() {
        while (true) {
                synchronized(queue) {
                    // 队列为空,线程被阻塞
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();

可以看到,虽然线程TimerThread已启动,但因队列为空,线程被阻塞(等待queue锁)。

以上是Timer timer = new Timer()的整个运行过程,继续看timer.schedule(alarm,delay,period)。

public class Timer {

    public void schedule(TimerTask task, long delay, long period) {
        sched(task, System.currentTimeMillis()+delay, -period);
    }

    private void sched(TimerTask task, long time, long period) {
        synchronized(queue) {
               synchronized(task.lock) {
                  task.nextExecutionTime = time;
                  task.period = period;
                  task.state = TimerTask.SCHEDULED;
                }
           // 将闹钟加入队列
            queue.add(task);
            // 此时正好满足条件,主线程释放queue锁,并唤醒TimerThread
            if (queue.getMin() == task)
                queue.notify();
        }
    }

从源码可看出,主线程正好满足queue.getMin() == task,此时将唤醒TimerThread线程(waiting)并释放queue锁。

下面再切换到TimerThread的运行场景。

private void mainLoop() {
    while (true) {
            synchronized(queue) {
                while (queue.isEmpty() && newTasksMayBeScheduled)
                    queue.wait();
                if (queue.isEmpty())
                    break;
                task = queue.getMin();
                synchronized(task.lock) {
                    currentTime = System.currentTimeMillis();
                    executionTime = task.nextExecutionTime;
                    // 已经到了执行时间
                    if (taskFired = (executionTime<=currentTime)) {
                        // ...重新定义下次闹钟执行时间
                    }
                }
                if (!taskFired)
                    // 执行时间未到,线程再次阻塞
                    queue.wait(executionTime - currentTime);
            }
            if (taskFired)
                task.run(); // 同步执行用户定义的闹钟
    }
}

通过上面的源码分析,TimerThread被唤醒后,将判断执行时间,时间到则初始化下次闹钟的执行时间并运行本次闹钟,否则线程将等待指定时间。

如此周而复始。。

11-25 22:35