Timer和TimerTask

JDK自带,具体的定时任务由TimerTask指定,定时任务的执行调度由Timer设定。Timer和TimerTask均在包java.util里实现。

本文基于java version "1.8.0_191"展开分析学习。

  1. TimerTask负责实现指定的任务

    1. 创建一个TimerTask实例。

      /**
      * Creates a new timer task.
      */
      protected TimerTask() {
      }
    2. 抽象方法,实现具体的任务

      /**
      * The action to be performed by this timer task.
      */
      public abstract void run();
    3. cancel方法,取消任务的调度,而不是任务的执行

      对于one-time execution和repeated execution任务,

      如果任务尚未执行,则对应的任务将永远不会执行;

      如果任务正在执行,则等待当前任务执行之后,再也不会被调度到。

      run方法中调用该方法,则任务不会再被调度。

      该方法可以反复调用,但只有第一次调用有效,后续调用无效;

    	/**
    * @return true if this task is scheduled for one-time execution and has
    * not yet run, or this task is scheduled for repeated execution.
    * Returns false if the task was scheduled for one-time execution
    * and has already run, or if the task was never scheduled, or if
    * the task was already cancelled. (Loosely speaking, this method
    * returns <tt>true</tt> if it prevents one or more scheduled
    * executions from taking place.)
    */
    public boolean cancel() {
    synchronized(lock) {
    boolean result = (state == SCHEDULED);
    state = CANCELLED;
    return result;
    }
    }
    1. scheduledExecutionTime方法返回此任务最近实际执行的已安排执行时间。

      (如果在任务执行过程中调用此方法,则返回值为此任务执行的已安排执行时间。)

      通常从一个任务的 run 方法中调用此方法,以确定当前任务执行是否能充分及时地保证完成已安排活动:
    	 /**
    * @return the time at which the most recent execution of this task was
    * scheduled to occur, in the format returned by Date.getTime().
    * The return value is undefined if the task has yet to commence
    * its first execution.
    * @see Date#getTime()
    */
    public long scheduledExecutionTime() {
    synchronized(lock) {
    return (period < 0 ? nextExecutionTime + period
    : nextExecutionTime - period);
    }
    }
  2. Timer负责任务的调度执行

    Timer类共有14个方法,其中4个构造方法,9个与调度相关的方法。

    1. 其调度是通过队列实现的,相关成员变量为:

      	/**
      * The timer task queue. This data structure is shared with the timer
      * thread. The timer produces tasks, via its various schedule calls,
      * and the timer thread consumes, executing timer tasks as appropriate,
      * and removing them from the queue when they're obsolete.
      */
      private final TaskQueue queue = new TaskQueue(); /**
      * The timer thread.
      */
      private final TimerThread thread = new TimerThread(queue); /**
      * This object causes the timer's task execution thread to exit
      * gracefully when there are no live references to the Timer object and no
      * tasks in the timer queue. It is used in preference to a finalizer on
      * Timer as such a finalizer would be susceptible to a subclass's
      * finalizer forgetting to call it.
      */
      private final Object threadReaper = new Object() {
      protected void finalize() throws Throwable {
      synchronized(queue) {
      thread.newTasksMayBeScheduled = false;
      queue.notify(); // In case queue is empty.
      }
      }
      };
    2. 其核心调度实现是private void sched(TimerTask task, long time, long period)

      其调度接口与实现分析如下:

      	/**
      * Schedule the specified timer task for execution at the specified
      * time with the specified period, in milliseconds. If period is
      * positive, the task is scheduled for repeated execution; if period is
      * zero, the task is scheduled for one-time execution. Time is specified
      * in Date.getTime() format. This method checks timer state, task state,
      * and initial execution time, but not period.
      *
      * @throws IllegalArgumentException if <tt>time</tt> is negative.
      * @throws IllegalStateException if task was already scheduled or
      * cancelled, timer was cancelled, or timer thread terminated.
      * @throws NullPointerException if {@code task} is null
      */
      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();
      }
      }
    3. 对外提供的接口

      • public void schedule(TimerTask task, Date time)
      Schedules the specified task for execution at the specified time.  If
      the time is in the past, the task is scheduled for immediate execution.
      @param task task to be scheduled.
      @param time time at which task is to be executed.
      • public void schedule(TimerTask task, Date firstTime, long period)
      Schedules the specified task for repeated fixed-delay execution,
      beginning at the specified time. Subsequent executions take place at
      approximately regular intervals, separated by the specified period.
      In fixed-delay execution, each execution is scheduled relative to
      the actual execution time of the previous execution. 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.
      Fixed-delay execution is appropriate for recurring activities
      that require "smoothness." In other words, it is appropriate for
      activities where it is more important to keep the frequency accurate
      in the short run than in the long run. This includes most animation
      tasks, such as blinking a cursor at regular intervals. It also includes
      tasks wherein regular activity is performed in response to human
      input, such as automatically repeating a character as long as a key
      is held down.
      @param task: task to be scheduled.
      @param firstTime: First time at which task is to be executed.
      @param period: time in milliseconds between successive task executions.
      • public void schedule(TimerTask task, long delay)
      Schedules the specified task for execution after the specified delay.
      • public void schedule(TimerTask task, long delay, long period)
      Schedules the specified task for repeated fixed-delay execution,
      beginning after the specified delay. Subsequent executions take place
      at approximately regular intervals separated by the specified period.
      In fixed-delay execution, each execution is scheduled relative to
      the actual execution time of the previous execution. 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).
      Fixed-delay execution is appropriate for recurring activities
      that require "smoothness." In other words, it is appropriate for
      activities where it is more important to keep the frequency accurate
      in the short run than in the long run. This includes most animation
      tasks, such as blinking a cursor at regular intervals. It also includes
      tasks wherein regular activity is performed in response to human
      input, such as automatically repeating a character as long as a key
      is held down.

      上述方法调用过程中,如果任务执行过程出现异常原因导致延时,比如某次任务处理时间过长,

      则后续任务将会依次延时。即以固定间隔执行任务调度

      • public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
      Schedules the specified task for repeated fixed-rate execution,
      beginning at the specified time. Subsequent executions take place at
      approximately regular intervals, separated by the specified period.
      In fixed-rate execution, each execution is scheduled relative to th
      scheduled execution time of the initial execution. 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). As a
      consequence of the above, if the scheduled first time is in the past,
      then any "missed" executions will be scheduled for immediate "catch up
      execution.
      Fixed-rate execution is appropriate for recurring activities that
      are sensitive to <i>absolute</i> time, such as ringing a chime every
      hour on the hour, or running scheduled maintenance every day at a
      particular time. It is also appropriate for recurring activities
      where the total time to perform a fixed number of executions is
      important, such as a countdown timer that ticks once every second for
      ten seconds. Finally, fixed-rate execution is appropriate for
      scheduling multiple repeating timer tasks that must remain synchronize
      with respect to one another.
      • public void scheduleAtFixedRate(TimerTask task, long delay, long period)
      Schedules the specified task for repeated fixed-rate execution,
      beginning after the specified delay. Subsequent executions take place
      at approximately regular intervals, separated by the specified period.
      In fixed-rate execution, each execution is scheduled relative to the
      scheduled execution time of the initial execution. 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).
      Fixed-rate execution is appropriate for recurring activities that
      are sensitive to absolute time, such as ringing a chime every
      hour on the hour, or running scheduled maintenance every day at a
      particular time. It is also appropriate for recurring activities
      where the total time to perform a fixed number of executions is
      important, such as a countdown timer that ticks once every second for
      ten seconds. Finally, fixed-rate execution is appropriate for
      scheduling multiple repeating timer tasks that must remain synchronized
      with respect to one another.

      上述方法调用过程中,如果出现某次任务执行时间过长,则后续任务会加快执行频率,

      可能会出现连续多次调用,"跟"上进度。即以近似固定频率执行任务调度。

  3. Deamon

    source code:

    import java.util.Timer;
    import java.util.TimerTask;
    import static java.lang.Thread.sleep; class TimerTaskDeamon extends TimerTask
    {
    private String jobName = "";
    public TimerTaskDeamon(String jobName)
    {
    super();
    this.jobName = jobName;
    }
    @Override
    public void run() {
    System.out.println("--------------->");
    System.out.println("execute " + "currentTimeMillis" + ": " + System.currentTimeMillis());
    System.out.println("execute " + "scheduledExecutionTime" + ": " + scheduledExecutionTime());
    System.out.println("execute " + jobName + ": ");
    try {
    sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("execute " + "currentTimeMillis" + ": " + System.currentTimeMillis());
    System.out.println("execute " + "scheduledExecutionTime" + ": " + scheduledExecutionTime());
    System.out.println("<---------------"); }
    }
    public class timerDeamon {
    static Timer timer = new Timer();
    public static void main(String[] args)
    {
    long delay = 1 * 1000;
    long period = 1000;
    timer.schedule(new TimerTaskDeamon("job1"), delay, period);
    timer.schedule(new TimerTaskDeamon("job2"),
    2*delay, 2*period);
    }
    }

    output:

    --------------->
    execute currentTimeMillis: 1544331346389
    execute scheduledExecutionTime: 1544331346389
    execute job1:
    execute currentTimeMillis: 1544331351401
    execute scheduledExecutionTime: 1544331346389
    <---------------
    --------------->
    execute currentTimeMillis: 1544331351401
    execute scheduledExecutionTime: 1544331351401
    execute job2:
    execute currentTimeMillis: 1544331356403
    execute scheduledExecutionTime: 1544331351401
    <---------------
  4. TaskQueue实现

     * This class represents a timer task queue: a priority queue of TimerTasks,
    * ordered on nextExecutionTime. Each Timer object has one of these, which it
    * shares with its TimerThread. Internally this class uses a heap, which
    * offers log(n) performance for the add, removeMin and rescheduleMin
    * operations, and constant time performance for the getMin operation.

    关键成员变量:private TimerTask[] queue = new TimerTask[128];

    支持任务的添加、删除和重新调度等操作。

    Priority queue represented as a balanced binary heap: the two children of queue[n] are queue[2*n] and queue[2*n+1].  The priority queue is ordered on the nextExecutionTime field: The TimerTask with the lowest nextExecutionTime is in queue[1] (assuming the queue is nonempty).  For each node n in the heap, and each descendant of n, d, n.nextExecutionTime <= d.nextExecutionTime.
  5. TimerThread

    TimerThread继承自Thread类。

    This "helper class" implements the timer's task execution thread, which  waits for tasks on the timer queue, executions them when they fire, reschedules repeating tasks, and removes cancelled tasks and spent non-repeating tasks from the 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) {
    // 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) {
    }
    }
    }

JDK自带Timer和TimerTask可以实现任务定时调度,包括定时执行、固定间隔执行、

固定频率执行,简单易用。但有三个缺点:

  1. 任务调度基于绝对时间而非相对时间,对系统时间敏感、依赖性强。
  2. 任务调度依赖同一个线程,必须串行执行,任务间依赖性强。
  3. 若单次任务执行过程中出现异常IllegalArgumentExceptionIllegalStateExceptionNullPointerException,会导致后续任务(不管是否已经安排调度)执行失败。

Java中定时器相关实现的介绍与对比之:Timer和TimerTask的更多相关文章

  1. 【Socket编程】Java中网络相关API的应用

    Java中网络相关API的应用 一.InetAddress类 InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址. InetAddress类没有构造方法,所以不能直接new出 ...

  2. Java中定时器Timer致命缺点(附学习方法)

    简介 这篇文章我一直在纠结到底要不要写,不想写一来因为定时器用法比较简单,二来是面试中也不常问.后来还是决定写了主要是想把自己分析问题思路分享给大家,让大家在学习过程中能够参考,学习态度我相信大部分人 ...

  3. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    Java 中15种锁的介绍 Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等,在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类 ...

  4. Java中15种锁的介绍

    作者:搜云库技术团队 原文:https://segmentfault.com/a/1190000017766364 1. Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观 ...

  5. java中的各种锁详细介绍

    转自:https://blog.csdn.net/axiaoboge/article/details/84335452 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高 ...

  6. 关于Java中final关键字的详细介绍

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使 ...

  7. Java中IO软件包的详细介绍

    一.Java Io流 Java Io流的概念 java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作.在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为" ...

  8. JAVA中取余(%)规则和介绍

    在java中%的含义为取余. java :a%b 数学公式a%b=a-(a/b)*b

  9. Android(java)学习笔记167:Java中操作文件的类介绍(File + IO流)

    1.File类:对硬盘上的文件和目录进行操作的类.    File类是文件和目录路径名抽象表现形式  构造函数:        1) File(String pathname)       Creat ...

随机推荐

  1. Python 学习笔记(九)Python元组和字典(二)

    什么是字典 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值 key=>value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 键必须是唯 ...

  2. 插入排序_c++

    插入排序_c++ GitHub 文解 插入排序的核心思想是针对于 N 个元素进行排序时,共进行 K = (N-1) 次排序,第 M 次排序时将第 M + 1 个元素插入前 M 个元素中进行排序. 图解 ...

  3. HDU 5536--Chip Factory(暴力)

    Chip Factory Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)T ...

  4. java枚举常见用法

    用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. p ...

  5. Django templates加载css/js/image等静态资源

    配置步骤: 1.首先在应用下面创建static目录 2.将静态资源拷贝进去 3.在应用的settings.py文件中添加 import os BASE_PATH = os.path.dirname(o ...

  6. telent connection refused

    1.问题场景 Centos7 做flume案例时,telnet hadoop-senior03.itguigu.com 44444 总是Connection redused, Trying 192.1 ...

  7. C语言调整数组使奇数全部都位于偶数前面

    //输入一个整数数组,实现一个函数,//来调整该数组中数字的顺序使得数组中所有的奇数 位于数组的前半部分,//所有偶数 位于数组的后半部分. #include<stdio.h>#inclu ...

  8. pyhton 下 使用getch(), 输入字符无需回车

    原代码来自 https://code.activestate.com/recipes/134892-getch-like-unbuffered-character-reading-from-stdin ...

  9. create a nodejs npm package

    1. create a folder named m1 2. run command: npm init, this will create the package.json file 3. crea ...

  10. 20154327 EXP8 Web基础

    基础问题回答 (1)什么是表单? 表单:表单在网页中主要负责数据采集功能.一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法. 表单域:包 ...