admin管理员组

文章数量:1531479

Java多线程核心技术5 - Timer 定时任务、schedule 与 scheduleAtFixedRate

  • 5. Timer 定时器
    • 5.1 定时器Timer
      • 计划时间早于当前时间
      • 多个TimerTask任务与延时
    • 5.2 schedule vs. scheduleAtFixedRate
      • 比较 schedule 与 scheduleAtFixedRate

来自阅读Java多线程编程核心技术的读书笔记,按照自己思路写了一些整理
越读越感觉这本书是一本“口水书”,有堆砌代码案例的嫌疑,看上去感觉更像是一本“博文集”。不是很建议作为自学并发的书籍,但是可以作为初步了解Java并发的窗口,初步熟悉Java中遇到的一些基本方法
历史内容:
Java多线程核心技术1 - 多线程技能
Java多线程核心技术2 - 对象和变量的并发访问
Java多线程核心技术3 - 线程间通信
Java多线程核心技术4 - ReentrantLock 和 ReentrantReadWriteLock 的使用

5. Timer 定时器

总结:

  • Timer是定时器,用于安排任务本质是一个线程
    TimerTask抽象任务本身
  • 一个Timer上可以安排(schedule)多个定时任务,实质是多个定时任务在一个线程顺序执行
    因此,若前面的定时任务执行时间较长,可能拖后临近执行的后面的定时任务

5.1 定时器Timer

Timer类负责计划任务的功能,即在指定的时间开始执行一个任务:

  • Timer类用于设置计划任务,设置计划任务的内容(TimerTask)、设置任务的启动时间、周期等
  • TimerTask类是Timer类的一个抽象子类,用于封装任务的内容

先来看下基本使用方式:

public class TimerTest {
  private static final Timer timer = new Timer();

  static class MyTask extends TimerTask {
    @Override
    public void run() {
      System.out.println("Run at " + new Date());
    }
  }

  public static void main(String[] args) throws ParseException {
    MyTask task = new MyTask();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = sdf.parse("2021-01-28 11:00:00");
    timer.schedule(task, date);
  }
}

当计划任务完成后,进程还没有销毁:因为创建一个Timer就是启动一个新的线程,这个新启动的线程并不是守护线程,所以将一直运行下去

若将Timer设置成守护线程:

// 源码: public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }
private static final Timer timer = new Timer(true);

根据守护线程的定义,当进程中的其他线程运行结束后,Timer线程也将随之销毁
若运行结束前,Timer中计划的任务TimerTask还未执行,则也不会再被执行

计划时间早于当前时间

在设置Timer和定时任务时,若计划时间早于当前时间,则立即执行任务

// 计划时间早于当前时间
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2000-01-28 11:00:00");
timer.schedule(task, date);		// 立即执行

多个TimerTask任务与延时

对于同一个Timer定时器,可以为之设置多个TimerTask定时任务

MyTask task_1 = new MyTask();
MyTask task_2 = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date_1 = sdf.parse("2021-01-28 11:00:00");
Date date_2 = sdf.parse("2021-02-28 11:00:00");
// 安排多个定时任务
timer.schedule(task_1, date_1);
timer.schedule(task_2, date_2);

多个TimerTask是以队列的形式按顺序被执行的,所以执行时间可能与预期时间不一致,因为前面的任务执行时间可能较长,则后面的任务运行时间被推迟

5.2 schedule vs. scheduleAtFixedRate

方法解释
schedule(TimerTask task, Date time)在time时间点执行task任务
schedule(TimerTask task, Date firstTime, long period)在time时间点执行第一次task任务
以后按period时间间隔(ms)重复执行任务
schedule(TimerTask task, long delay)以当前时间为参考时间,延迟delay ms后执行一次task
schedule(TimerTask task, long delay, long period)以当前时间为参考时间,延迟delay ms后执行一次task
以后每隔period执行一次
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)以固定时间间隔执行任务,详情见下面解释

比较 schedule 与 scheduleAtFixedRate

  • schedule:fixed-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.

    • schedule方法带delay时,指的是fixed-delay,即根据前序任务实际执行时间安排后序任务的执行

      若前序任务因为某些原因被延迟执行,则后序任务也会被延迟执行

      后序任务执行时间根据前序任务结束时间计算(——《Java多线程编程核心技术》)

    • 常用于动画刷新任务、对人的输入响应任务等

  • scheduleAtFixedRate:fixed-rate

    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.

    • scheduleAtFixedRate方法带delay时,指的是fixed-rate,即参照设定的时间点向后推移period

      若前序任务被延迟执行,后序任务将间隔较快以赶上设定的时间点

    • 常用于精确定时执行的任务,如时钟每小时报时

本文标签: 核心技术多线程JavascheduleAtFixedRateschedule