相关推荐recommended
一文详解SpringBoot 定时任务(cron表达式)
作者:mmseoamin日期:2024-04-29

IDE:IntelliJ IDEA 2022.2.3 x64

操作系统:win10 x64 位 家庭版

JDK: 1.8


文章目录

  • 一、如何开启一个SpringBoot定时任务?
  • 二、cron表达式详解
    • 2.1 语法格式
    • 2.2 符号解析
      • 2,2.1 通用符号: , - * /
      • 2.2.2 专有符号:?L w # c
      • 2.3 测试运行
      • 三、fixedRate & fixedDelay
        • 3.1 fixedRate
        • 3.2 fixedDelay
        • 四、如何异步多线程的执行SprigBoot定时任务?
        • 资料参考

          提示:以下是本篇文章正文内容,下面案例可供参考

          一、如何开启一个SpringBoot定时任务?

          在我们日常开发中,开启并使用一个SpringBoot定时任务的大致步骤通常按如下所示

          步骤

          ①在启动类上添加@EnableScheduling开启定时任务

          示例代码如下

          //开启定时任务
          @EnableScheduling
          @SpringBootApplication
          public class MySpringBootApplication {
              public static void main(String[] args) {
                  SpringApplication.run(MySpringBootApplication.class, args);
              }
          }
          

          ②编写定时任务,在其上添加@Scheduled设置任务执行时间

          示例代码如下

           //每3秒执行一次
              @Scheduled(fixedRate = 3000)
              public void notifyA(){
                  String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                  String name = Thread.currentThread().getName();
                  System.out.println(name+"通知已发送"+",发送时间为"+now);
              }
          

          ③将定时任务所在的类上添加@Component

          示例代码如下

          @Component
          public class springTimerTask {
              //每3秒执行一次
              @Scheduled(fixedRate = 3000)
              public void notifyA(){
                  String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                  String name = Thread.currentThread().getName();
                  System.out.println(name+"通知已发送"+",发送时间为"+now);
              }
          }
          

          运行如下

          一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第1张


          二、cron表达式详解

          在日常开发中,可以借助下面的网站快速生成cron表达式,提高开发效率

          👉cron表达式在线生成器

          2.1 语法格式

          形式*******
          字段SecondsMinutesHoursDayofMonthMonthDayofWeekYear
          含义星期年(可选)
          范围0-590-590-231-311-12(JAN-DEC)1-7 (SUN-SAT)
          符号, - * /, - * /, - * /, - * /, - * / ? L C #, - * /, - * / ? L C #

          备注

          ①DayofWeek字段:有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一,余下依次类推

          ②Year字段:有效范围为1970-2099年

          2.2 符号解析

          2,2.1 通用符号: , - * /

          • ,:表示列出枚举值。例如:在Minutes域使用5,20,表示在时间的分钟数为5、20时触发事件
          • -:表示范围。例如在Minutes域使用5-20,表示在时间的分钟数为5到20时每分钟都触发事件
          • *:表示匹配该域的任意值。假如在Minutes:域使用,表示时间分钟数不做限制,每分钟都触发事件
          • /:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,表示时间的分钟数为5时触发一次,后隔20分钟即25、45再分别触发一次事件

            举例说明

            • 0/2 * * * * ? : 表示每2秒 执行任务
            • 0 0/2 * * * ? : 表示每2分钟 执行任务
            • 0 0 2 1 * ? : 表示在每月的1日的凌晨2点调整任务

              2.2.2 专有符号:?L w # c

              • ?:只能用在DayofMonth和DayofWeek两个域,由于DayofMonthi和DayofWeek互斥,须对其一设置?
              • L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用L,意味着在最后的一个星期四触发
              • W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件
              • WL:这两个字符可以连用,表示在某个月最后一个工作日
              • #:用于确定每个月第几个星期几,只能出现在DayofWeek域。例如在4#2,表示某月的第二个星期三
              • C:只能用在DayofMonth和DayofWeeki两个域,需要关联日历,如果没关联可以忽略

                举例说明

                • 0 0-5 14 * * ? : 在每天下午2点到下午2:05期间的每1分钟触发
                • 0 10,44 14 ? 3 WED : 每年三月的星期三的下午2:10和2:44触发
                • 0 15 10 ? * MON-FRI : 周一至周五的上午10:15触发
                • 0 15 10 15 * ? : 每月15日上午10:15触发
                • 0 15 10 L * ? : 每月最后一日的上午10:15触发
                • 0 15 10 ? * 6L : 每月的最后一个星期五上午10:15触发
                • 0 15 10 ? * 6L 2002-2005: 2002年至2005年的每月的最后一个星期五上午10:15触发

                  注意

                  springBoot只支持cron表达式中的专有符号?,也仅仅支持6位的cron表达式,7位的不支持!!!

                  2.3 测试运行

                  示例代码如下

                   //每2秒执行一次
                      @Scheduled(cron = "0/2 * * * * ?")
                      public void notifyA(){
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                  

                  运行如下

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第2张


                  三、fixedRate & fixedDelay

                  如下可知,@Schedule里还可以使用以下两种类型的表达式,分别是“fixedDelay = xxx” 与 “fixedRate = xxx”

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第3张

                  3.1 fixedRate

                  fixedRate属性是@Schedule注解中的一个属性,它表示以固定的频率执行某个方法或任务。此属性用于指定任务执行的时间间隔,单位为毫秒。

                  例如,如果设置为2000(即2秒),则任务将每隔2秒执行一次。

                  示例代码如下

                  //每2秒执行一次
                      @Scheduled(fixedRate = 2000)
                      public void notifyB(){
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                  

                  运行如下

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第4张

                  注意

                  fixedRate是从上一次方法执行开始的时间算起,如果上一次方法阻塞住了,下一次也是不会执行,但是在阻塞这段时间内累计应该执行的次数,当不再阻塞时,一下子把这些全部执行掉,而后再按照固定速率继续执行。

                  测试代码如下

                  	//模拟任务阻塞4s,每2秒执行一次
                      @Scheduled(fixedRate = 2000)
                      public void notifyB() throws InterruptedException {
                          Thread.sleep(4000);
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                  

                  运行如下

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第5张

                  3.2 fixedDelay

                  fixedDelay属性是@Schedule注解中的一个属性,它表示以固定的时间间隔执行某个方法或任务,并在每次执行完成后等待指定的延迟时间再执行下一次。

                  fixedDelay属性用于指定任务执行之间的延迟时间,单位为毫秒。

                  例如,如果设置为2000(即2秒),则任务将每隔2秒执行一次,并且在每次执行完成后等待2秒再执行下一次。

                  示例代码如下

                     //每2秒执行一次,并延迟2s
                      @Scheduled(fixedDelay = 2000)
                      public void notifyB(){
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                  

                  运行如下

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第6张

                  注意

                  fixedDelay的执行时间是以上一次方法执行完开始算起,比如上一次方法执行阻塞住了,那么直到上一次执行完,并间隔给定的时间后,执行下一次

                  举个具体的例子,如果在一个方法上设置了fixedDelay=5*1000(即5秒),那么当该方法某一次执行结束后,开始计算时间,当时间达到5秒,就开始再次执行该方法。这意味着无论任务实际执行需要多长时间,每个任务之间的延迟时间始终保持不变。

                  测试代码如下

                  	//模拟上次任务阻塞4s,然后间隔2秒执行下一次
                      @Scheduled(fixedDelay = 2000)
                      public void notifyB() throws InterruptedException {
                          Thread.sleep(4000);
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                  

                  运行如下

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第7张


                  四、如何异步多线程的执行SprigBoot定时任务?

                  最简单的做法如下所示

                  步骤

                  ①在启动类上添加注解@EnableAsync开启异步执行

                  示例代码如下

                  @EnableScheduling
                  @SpringBootApplication
                  @EnableAsync
                  public class MySpringBootApplication {
                      public static void main(String[] args) {
                          SpringApplication.run(MySpringBootApplication.class, args);
                      }
                  }
                  

                  ②在定时任务上添加@Asynci设置异步执行

                  示例代码如下

                  @Component
                  public class springTimerTask {
                      //每2秒执行一次
                      @Async
                      @Scheduled(cron = "0/2 * * * * ?")
                      public void notifyA(){
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                      //每3秒执行一次
                      @Async
                      @Scheduled(cron = "0/3 * * * * ?")
                      public void notifyB() throws InterruptedException {
                          String now = new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
                          String name = Thread.currentThread().getName();
                          System.out.println(name+"通知已发送"+",发送时间为"+now);
                      }
                  }
                  

                  运行如下

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第8张

                  why?

                  Spring默认单线程的定时任务,如果不开启异步,多个任务都是同一个线程在处理,如果这个线程内的任务比较耗时,会导致后续任务延期; 开启异步后,每个任务都会从线程池里分配一个线程去完成,避免耗时长的任务,导致其他任务延期,故而上述代码在中存在多个线程在执行同一个任务的情况;

                  不信?请看如下不开启异步,多个定时任务同时运行的情形

                  一文详解SpringBoot 定时任务(cron表达式),在这里插入图片描述,第9张


                  资料参考

                  https://www.bilibili.com/video/BV1JR4y1N7Ni/?p=7&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=5a34715e416a427a73a3ca52397848b5

                  https://blog.csdn.net/u011066470/article/details/107529863?ops_request_misc=&request_id=&biz_id=102&utm_term=SpringBoot%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1:%20cron%E8%A1%A8%E8%BE%BE%E5%BC%8F&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-107529863.142v96pc_search_result_base9&spm=1018.2226.3001.4187