前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JDK Timer 实现原理分析

JDK Timer 实现原理分析

作者头像
Yano_nankai
发布2018-10-08 10:31:19
4430
发布2018-10-08 10:31:19
举报
文章被收录于专栏:二进制文集二进制文集

说明

本文分析的是 JDK 7

Timer 基本用法

代码语言:javascript
复制
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);
}

输出:

代码语言:javascript
复制
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 的状态

代码语言:javascript
复制
/**
 * 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

函数的定义如下:

代码语言:javascript
复制
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 取反了。如果某次执行任务的开始时间延后了,那么此后的每次任务都会延迟。

代码语言:javascript
复制
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 则并不会这样。

代码语言:javascript
复制
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)
代码语言:javascript
复制
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 核心方法

代码语言:javascript
复制
/**
 * 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 对应的时间。

其最核心的代码:

代码语言:javascript
复制
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 线程,这个线程无限循环一个小顶堆的任务队列,每次取出最近需要执行的任务,如果符合条件则对该任务(小顶堆)做相应处理。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.07.02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 说明
  • Timer 基本用法
  • 实现原理
    • TimerTask 的状态
      • Timer 的两个方法
        • TimerTask 核心方法
        • 费曼技巧:概述 Timer 的实现原理
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档