美文网首页程序员
JDK Timer 实现原理分析

JDK Timer 实现原理分析

作者: 被称为L的男人 | 来源:发表于2018-07-02 15:40 被阅读147次

说明

本文分析的是 JDK 7

Timer 基本用法

public static void main(String[] args) {
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        
        @Override
        public void run() {
            System.out.println(new Date(this.scheduledExecutionTime()));
        }
        
    }, 500L, 1000L);
}

输出:

Mon Jul 02 14:34:20 CST 2018
Mon Jul 02 14:34:21 CST 2018
Mon Jul 02 14:34:22 CST 2018
Mon Jul 02 14:34:23 CST 2018
Mon Jul 02 14:34:24 CST 2018
Mon Jul 02 14:34:25 CST 2018

主要能够指定定时任务的初始延迟、间隔执行时间。

实现原理

首先画了一张示意图,能够说明 Timer 的基本原理。

大体上有 4 个类:

  • Timer:定时器主类
  • TimerTask:实现了 Runnable 的抽象类(run 仍为抽象方法,需要用户实现),定义了与 Timer 特有的任务状态(下一次执行时间 nextExecutionTime 和 执行时间间隔 period)
  • TimerThread:由 Timer 启动,里面有个无限循环的 mainLoop 方法,用来取出 TaskQueue 中最近需要执行的任务,判断是否需要执行。
  • TaskQueue:由 TimerTask 组成的小顶堆,其排序是根据 TimerTask 的 nextExecutionTime。

TimerTask 的状态

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

Timer 的两个方法

  • schedule
  • scheduleAtFixedRate

函数的定义如下:

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);
}

public void scheduleAtFixedRate(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);
}

注意其中最大的区别,在于 schedule 调用 sched 函数时,将传入的 period 取反了。如果某次执行任务的开始时间延后了,那么此后的每次任务都会延迟。

If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well. In the long run, the frequency of execution will generally be slightly lower than the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate). As a consequence of the above, if the scheduled first time is in the past, it is scheduled for immediate execution. 

而 scheduleAtFixedRate 则并不会这样。

If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up." In the long run, the frequency of execution will be exactly the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate). 

其加入后的核心函数是 sched。参数 period

  • 0:重复任务的时间间隔(scheduleAtFixedRate)

  • =0:仅执行一次
  • <0:重复任务的时间间隔(schedule)
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();
    }
}

TimerTask 核心方法

/**
 * 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) {
        }
    }
}

整体流程是:获取 queue 的锁,若 queue 不为空,则拿出第一个任务,获取任务的锁,若任务的状态正常且到执行时间,则执行任务;否则将其 wait 对应的时间。

其最核心的代码:

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);
    }
}

费曼技巧:概述 Timer 的实现原理

Timer 是 JDK 自带的执行定时任务的工具类,用户能够指定延迟、任务执行的时间间隔。当 new 一个 Timer 时,会自动启动一个 TimerTask 线程,这个线程无限循环一个小顶堆的任务队列,每次取出最近需要执行的任务,如果符合条件则对该任务(小顶堆)做相应处理。

相关文章

  • JDK Timer 实现原理分析

    说明 本文分析的是 JDK 7 Timer 基本用法 输出: 主要能够指定定时任务的初始延迟、间隔执行时间。 实现...

  • 【原创】camel Timer组件使用

    【原创】camel Timer组件,分析其源代码,是基于JDK java.util.Timer类的功能实现。 用于...

  • Timer和TimerTask

    关于Timer定时器的实现原理,如果我们看过JDK源码,就会发现,是使用的Object.wait(timeout)...

  • AOP——cglib动态代理源码解析

    上一篇分析了使用JDK动态代理的低层实现原理,这次再来看看cglib实现动态代理的原理。 关于JDK动态代理的实现...

  • Java Timer源码分析

    通过源码分析,我们可以更深入的了解其底层原理。 对于JDK自带的定时器,主要涉及TimerTask类、Timer类...

  • RxSwift timer与Swift timer比较和原理探索

    RxSwift中timer不受runloop的影响可以正常计时,以下探索timer实现原理。 Timer swif...

  • Java 定时任务(含Spring定时任务)

    1:Timer Timer 是 JDK 自带的定时任务执行类,无论任何项目都可以直接使用 Timer 来实现定时任...

  • ConcurrentHashMap源码解析

    ConcurrentHashMap源码解析 [TOC] jdk8之前的实现原理 jdk8的实现原理 JDK8的实现...

  • ConcurrentHashMap源码解析

    ConcurrentHashMap源码解析 [TOC] jdk8之前的实现原理 jdk8的实现原理 JDK8的实现...

  • JDK Timer实现详解

    概述 本文首发于我的个人博客JDK Timer实现详解,严禁转载。定时器是工程开发中比较常用的工具,本文研究JDK...

网友评论

    本文标题:JDK Timer 实现原理分析

    本文链接:https://www.haomeiwen.com/subject/tndluftx.html