当前位置: 代码迷 >> 综合 >> JDK TimerTask 定时器常用方法
  详细解决方案

JDK TimerTask 定时器常用方法

热度:60   发布时间:2023-12-08 12:43:18.0

文章目录

        • 一、JDK 自带定时器
        • 二、JDK 自带定时器的实现方式
        • 三、JDK 自带定时器的问题描述
        • 四、针对问题的解决方案

一、JDK 自带定时器
  • JDK 的工具类中有一个 Timer 类用于定时任务的业务实现

  • 根据 Timer.schedule 的重载方法,可以实现不同机制的任务调度

  • 接下来演示不同机制都具体表现以及存在都问题和解决方案

二、JDK 自带定时器的实现方式
  • 代码实现

    /*** Copyright (C), 1998-2021, Shenzhen Rambo Technology Co., Ltd* JDK 自带调度任务** 弊端:* 任务调度都采用同一线程池同一线程* 如果一旦某个任务响应时间过长,将会导致线程阻塞(单线程执行多个任务)** @author Rambo* @date 2021/2/22 17:05* @since 1.0.0.1*/
    @Slf4j
    public class TimerScheduleTasks {
          // 定义任务调度器实例对象private static final Timer timer = new Timer();public static void main(String[] args) {
          timer1();timer2();timer3();timer4();}/*** 方法一:* schedule(TimerTask task, long delay, long period);* 设定指定任务 task 在指定延迟 delay 后进行固定延迟 period 的执行* 0ms之后开始执行,每隔 1000 ms执行一次** @author Rambo* @date 2021/2/22 17:19*/public static void timer1(){
          timer.schedule(new TimerTask(){
          @SneakyThrows@Overridepublic void run(){
          log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer1()],执行频率:1秒/次,当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 0 , 1000);}/*** 方法二:* schedule(TimerTask task, Date time);* 设定指定任务 task 在指定时间 time 后执行一次* 5ms 后执行一次 run 方法后结束,执行完成后线程处于挂起等待状态** @author Rambo* @date 2021/2/22 17:19*/public static void timer2(){
          timer.schedule(new TimerTask(){
          @SneakyThrows@Overridepublic void run(){
          log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer2()],当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 5000);}/*** 方法三:* scheduleAtFixedRate(TimerTask task, long delay, long period);* 设定指定任务 task 在指定延迟 delay 后进行固定频率 period 的执行* 0ms 后,每隔 2s 执行一次* @author Rambo* @date 2021/2/22 17:39*/public static void timer3() {
          timer.scheduleAtFixedRate(new TimerTask() {
          @SneakyThrows@Overridepublic void run() {
          log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer3()],执行频率:2秒/次,当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 0, 2000);}/*** 方法四:* scheduleAtFixedRate(TimerTask task, Date firstTime, long period);* 设定指定的任务 task 在指定的时间 firstTime 开始进行重复的固定速率 period 执行* 每天在预设时间去执行,执行完成后线程处于挂起等待状态** @author Rambo * @date 2021/2/22 17:45*/public static void timer4(){
          Calendar calendar = Calendar.getInstance();// 控制时calendar.set(Calendar.HOUR_OF_DAY , 17);// 控制分calendar.set(Calendar.MINUTE , 51);// 控制秒calendar.set(Calendar.SECOND , 0);Date time = calendar.getTime();SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss");log.info("任务计划执行时间:[{}]", format.format(time));timer.scheduleAtFixedRate(new TimerTask(){
          @SneakyThrows@Overridepublic void run(){
          log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer4()],执行频率:每天固定时间执行,当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}// 这里设定将延时每天固定执行}, time, 1000 * 60 * 60 * 24);}
    }
    

    1

三、JDK 自带定时器的问题描述
  • 任务调度都采用同一线程池同一线程

  • 如果一旦某个任务响应时间过长,将会导致线程阻塞(单线程执行多个任务)

四、针对问题的解决方案
  • 方案一(不推荐:只解决线程问题,没有解决线程阻塞问题)

    1. 由于上述案例代码中,所有的调度任务都采用同一个 Timer 实例对象,所以多个任务同时协作时,使用的是同一个线程

      ... ...
      public static void timer2(){
              // 每个调度任务都用新的实例去创建Timer timer = new Timer();timer.schedule(new TimerTask(){
              @SneakyThrows@Overridepublic void run(){
              log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer2()],当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 5000);
      }      
      ... ...
      
    2. 通过以上分析,提出的解决方案是在触发每一个调度任务都采用新的 Timer 实例,则可以采用不同的线程去调度

    3. 运行效果
      2

    弊端

    • 每一次调度都采用新的 Timer 实例对象来创建,可以解决同一线程问题,但是阻塞问题并未解决,原因是阻塞方法还是只能通过这个实例来调度
  • 方案二(推荐)

    1. 采用 ThreadPoolTaskExecutor 对象自定义线程池的方式结合 @Async 注解来实现

    2. 采用 ThreadPoolTaskScheduler 对象自定义线程池的方式结合 @Scheduled 注解来实现

    3. 以上介绍的两种方式,将在后续文章给出具体实现