[关闭]
@Yano 2018-07-02T07:39:57.000000Z 字数 4753 阅读 683

JDK Timer 实现原理分析

Java


说明

本文分析的是 JDK 7

Timer 基本用法

  1. public static void main(String[] args) {
  2. Timer timer = new Timer();
  3. timer.scheduleAtFixedRate(new TimerTask() {
  4. @Override
  5. public void run() {
  6. System.out.println(new Date(this.scheduledExecutionTime()));
  7. }
  8. }, 500L, 1000L);
  9. }

输出:

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 的状态

  1. /**
  2. * The state of this task, chosen from the constants below.
  3. */
  4. int state = VIRGIN;
  5. /**
  6. * This task has not yet been scheduled.
  7. */
  8. static final int VIRGIN = 0;
  9. /**
  10. * This task is scheduled for execution. If it is a non-repeating task,
  11. * it has not yet been executed.
  12. */
  13. static final int SCHEDULED = 1;
  14. /**
  15. * This non-repeating task has already executed (or is currently
  16. * executing) and has not been cancelled.
  17. */
  18. static final int EXECUTED = 2;
  19. /**
  20. * This task has been cancelled (with a call to TimerTask.cancel).
  21. */
  22. static final int CANCELLED = 3;

Timer 的两个方法

函数的定义如下:

  1. public void schedule(TimerTask task, long delay, long period) {
  2. if (delay < 0)
  3. throw new IllegalArgumentException("Negative delay.");
  4. if (period <= 0)
  5. throw new IllegalArgumentException("Non-positive period.");
  6. sched(task, System.currentTimeMillis()+delay, -period);
  7. }
  8. public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
  9. if (delay < 0)
  10. throw new IllegalArgumentException("Negative delay.");
  11. if (period <= 0)
  12. throw new IllegalArgumentException("Non-positive period.");
  13. sched(task, System.currentTimeMillis()+delay, period);
  14. }

注意其中最大的区别,在于 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)

  1. private void sched(TimerTask task, long time, long period) {
  2. if (time < 0)
  3. throw new IllegalArgumentException("Illegal execution time.");
  4. // Constrain value of period sufficiently to prevent numeric
  5. // overflow while still being effectively infinitely large.
  6. if (Math.abs(period) > (Long.MAX_VALUE >> 1))
  7. period >>= 1;
  8. synchronized(queue) {
  9. if (!thread.newTasksMayBeScheduled)
  10. throw new IllegalStateException("Timer already cancelled.");
  11. synchronized(task.lock) {
  12. if (task.state != TimerTask.VIRGIN)
  13. throw new IllegalStateException(
  14. "Task already scheduled or cancelled");
  15. task.nextExecutionTime = time;
  16. task.period = period;
  17. task.state = TimerTask.SCHEDULED;
  18. }
  19. queue.add(task);
  20. if (queue.getMin() == task)
  21. queue.notify();
  22. }
  23. }

TimerTask 核心方法

  1. /**
  2. * The main timer loop. (See class comment.)
  3. */
  4. private void mainLoop() {
  5. while (true) {
  6. try {
  7. TimerTask task;
  8. boolean taskFired;
  9. synchronized(queue) {
  10. // Wait for queue to become non-empty
  11. while (queue.isEmpty() && newTasksMayBeScheduled)
  12. queue.wait();
  13. if (queue.isEmpty())
  14. break; // Queue is empty and will forever remain; die
  15. // Queue nonempty; look at first evt and do the right thing
  16. long currentTime, executionTime;
  17. task = queue.getMin();
  18. synchronized(task.lock) {
  19. if (task.state == TimerTask.CANCELLED) {
  20. queue.removeMin();
  21. continue; // No action required, poll queue again
  22. }
  23. currentTime = System.currentTimeMillis();
  24. executionTime = task.nextExecutionTime;
  25. if (taskFired = (executionTime<=currentTime)) {
  26. if (task.period == 0) { // Non-repeating, remove
  27. queue.removeMin();
  28. task.state = TimerTask.EXECUTED;
  29. } else { // Repeating task, reschedule
  30. queue.rescheduleMin(
  31. task.period<0 ? currentTime - task.period
  32. : executionTime + task.period);
  33. }
  34. }
  35. }
  36. if (!taskFired) // Task hasn't yet fired; wait
  37. queue.wait(executionTime - currentTime);
  38. }
  39. if (taskFired) // Task fired; run it, holding no locks
  40. task.run();
  41. } catch(InterruptedException e) {
  42. }
  43. }
  44. }

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

其最核心的代码:

  1. currentTime = System.currentTimeMillis();
  2. executionTime = task.nextExecutionTime;
  3. if (taskFired = (executionTime<=currentTime)) {
  4. if (task.period == 0) { // Non-repeating, remove
  5. queue.removeMin();
  6. task.state = TimerTask.EXECUTED;
  7. } else { // Repeating task, reschedule
  8. queue.rescheduleMin(
  9. task.period<0 ? currentTime - task.period
  10. : executionTime + task.period);
  11. }
  12. }

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

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注