JDK TimerTask 定时器常用方法

一、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
                @Override
                public 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
                @Override
                public 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
                @Override
                public 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
                @Override
                public 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
              @Override
              public 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. 以上介绍的两种方式,将在后续文章给出具体实现

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值