@Scheduled注解简介

目录

一、注解介绍

二、注解参数

2.1. 参数简介

2.2. 主要参数说明

2.2.1. cron表达式

2.2.2. cron通配符

2.2.3. cron表达式示例

2.2.4. cron表达式参数数量解疑

2.3.注解源码

三、cron生成

四、@Scheduled注解使用建议

4.1.建议 

4.2.拓展介绍 

五、易错点解释 

5.1 "年"的使用 

5.2 #的使用 


一、注解介绍

        @Scheduled注解是Spring Boot提供的用于定时任务控制的注解,主要用于控制任务在某个指定时间执行,或者每隔一段时间执行。

二、注解参数

2.1. 参数简介

参数说明示例
cron 任务执行的cron表达式见下文
zonecron表达时解析使用的时区,默认为服务器的本地时区。
使用java.util.TimeZone#getTimeZone(String)方法解析
GMT-8:00
fixedRate固定速率
上一次任务执行开始到下一次执行开始的间隔时间固定,单位为ms。
若在调度任务执行时,上一次任务还未执行完毕,会加入worker队列,等待上一次执行完成后,马上执行下一次任务
1000
fixedRateString与fixedRate一致,只是间隔时间使用java.time.Duration#parse解析1000或PT1S
fixedDelay 固定延迟
上一次任务执行结束到下一次执行开始的间隔时间固定,单位为ms。
1000

fixedDelayString
与fixedDelay一致,只是间隔时间使用java.time.Duration#parse解析1000或PT1S
initialDelay 首次延迟多长时间后执行,单位ms。
之后按照fixedRate、fixedRateString、fixedDelay、fixedDelayString指定的规则执行,需要指定其中一个规则。
注意:不能和cron一起使用
1000
initialDelayString 与initialDelay 一致,只是间隔时间使用java.time.Duration#parse解析 1000或PT1S

2.2. 主要参数说明

        注意,@Scheduled需要配合@EnableScheduling使用。使用时,将@Scheduled注解放在待定时的方法名上方,将 @EnableScheduling放在项目主启动类类名上方。@Scheduled主要有三种配置执行时间的方式:cron、fixedRate和fixedDelay。

         关于 fixedRate 和 fixedDelay的区别:fixedRate 的间隔时间是上次任务开始后,开始计算时间间隔,达到指定时间间隔后,开始执行下一次任务;而 fixedDelay 的间隔时间是上次任务结束后,开始计算时间间隔,达到指定时间间隔后,开始执行下一次任务。

        例如:当一个任务需要统计大量数据,并根据不用用户生成不同文件报表,并将生成的报表推送到相应的用户下,该任务有补充机制。在执行该任务时,将使用大量时间时,此时使用 fixedRate 就会造成工作队列长时间堆积,同时,如果使用 fixedRate 的话,与 cron 的作用高度重合,基本可以用 cron 表达式替代,这个情况下,使用 fixedDelay 效果更好,这是在一个任务结束后,再次执行该任务进行重试,直到所有用户都拿到相应的文件。

2.2.1. cron表达式

cron@Scheduled的一个参数,是一个字符串,以空格隔开。

cron表达式格式:

@Scheduled(cron = "{秒数} {分钟} {小时} {日期} {月份} {星期}")

cron表达式示例:

@Scheduled(cron = "0 00 07 * * *")

 cron表达式可分为6或7个占位符,但在spring自带的定时任务中,cron只支持6个参数(详情请参考2.3的源码),若使用7个参数就会报错,示例如下:

        例一: 

@Scheduled(cron = "0/3 * * * * ? 2022-2023")
public void test(){

    logger.info("输出测试!");

}


org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name '类名' defined in file 
[编译后class文件路径]: Initialization of bean failed; 

nested exception is java.lang.IllegalStateException: 
Encountered invalid @Scheduled method 'test': Cron expression 
must consist of 6 fields (found 7 in "0/3 * * * * ? 2022-2023")

        例二: 

        例三: 

各参数介绍:

单位允许值允许通配符
毫秒0-59,  -  *  / 
分钟0-59,  -  *  / 
小时0-23,  -  *  / 
日期1-31,  -  *  /  ?  L  W
月份1-12或JAN-DEC(大小写均可),  -  *  /  ?
星期

1-7或SUN-SAT(大小写均可)

注:星期日为每周第一天,所以1-7表示周末到周六

,  -  *  /  ?  L  #

cron表达式各占位符解释:

{秒数}{分钟}{小时}{日期}{星期} ==> 不允许为空值,若值不合法,调度器将抛出SchedulerException异常

“/”代表触发步进(step),”/”前面的值代表初始值(""等同"0"),后面的值代表偏移量,比如

"0/20"或者"/20"代表从0秒钟开始,每隔20秒钟触发1次,即第0秒触发1次,第20秒触发1次,第40秒触发1次;

"5/20"代表第5秒触发1次,第25秒触发1次,第45秒触发1次;

"10-45/20"代表在[10,45]内步进20秒命中的时间点触发,即第10秒触发1次,第30秒触发1次

2.2.2. cron通配符

符号含义
*所有值,在秒字段上表示每秒执行,在月字段上表示每月执行
不指定值,不需要关心当前指定的字段的值,比如每天都执行但不需要关心周几就可以把周的字段设为?
-区间或者范围,如秒的0-2 ,表示0秒、1秒、2秒都会触发
,指定值,比如在0秒、20秒、25秒触发,可以把秒的字段设为0,20,25
/递增触发,比如秒的字段上设0/3 ,表示从第0秒开始,每隔3秒触发
L最后,只允许在日字段或周字段上,在日字段上使用L表示当月最后一天,在周字段上使用3L表示该月最后一个周二
W只允许用在日字段上,表示距离最近的该日的工作日,工作日指的是周一至周五
#只允许在周字段上,表示每月的第几个周几,如2#3 , 每月的第3个周二

2.2.3. cron表达式示例

@Scheduled(cron = "* * * * * *")示例:*每秒执行

@Scheduled(cron = "10-45/20 * * * * *")示例:-和/占位符的组合使用

@Scheduled(cron = "*/5 * * * * *")示例:*和/的使用,程序启动后,每5秒执行一次

@Scheduled(cron = "25/5 * * * * *")示例:与上述示例对比,从25秒起,每5秒执行一次

 其他示例:

“30 * * * * ?” 每半分钟触发任务 

“30 10 * * * ?” 每小时的10分30秒触发任务 

“30 10 1 * * ?” 每天1点10分30秒触发任务 

“30 10 1 20 * ?” 每月20号1点10分30秒触发任务 

“30 10 1 20 10 ? *” 每年10月20号1点10分30秒触发任务 

“30 10 1 20 10 ? 2011” 2011年10月20号1点10分30秒触发任务 

“30 10 1 ? 10 * 2011” 2011年10月每天1点10分30秒触发任务 

“30 10 1 ? 10 SUN 2011” 2011年10月每周日1点10分30秒触发任务 

“15,30,45 * * * * ?” 每15秒,30秒,45秒时触发任务 

“15-45 * * * * ?” 15到45秒内,每秒都触发任务 

“15/5 * * * * ?” 每分钟的每15秒开始触发,每隔5秒触发一次 

“15-30/5 * * * * ?” 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次 

“0 0/3 * * * ?” 每小时的第0分0秒开始,每三分钟触发一次 

“0 15 10 ? * MON-FRI” 星期一到星期五的10点15分0秒触发任务 

“0 15 10 L * ?” 每个月最后一天的10点15分0秒触发任务 

“0 15 10 LW * ?” 每个月最后一个工作日的10点15分0秒触发任务 

“0 15 10 ? * 5L” 每个月最后一个星期四的10点15分0秒触发任务 

“0 15 10 ? * 5#3” 每个月第三周的星期四的10点15分0秒触发任务

2.2.4. cron表达式参数数量解疑

cron表达式格式:在此,表达式以空格分为6或7个域

@Scheduled(cron = "{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}")

{年份} ==> 允许值范围: 1970~2099 ,允许为空,若值不合法,调度器将抛出SchedulerException异常

注意:除了{日期}和{星期}可以使用”?”来实现互斥,表达无意义的信息之外,其他占位符都要具有具体的时间含义,
且依赖关系为:年->月->日期(星期)->小时->分钟->秒数

2.3.注解源码

         由于该注解类比较长,所以采取分段截取的方式

2.3 图一 
2.3 图二 
2.3 图三

 

2.3 图四
2.3 图五
2.3 图六
2.3 图七

 

三、cron生成

         在在线Cron表达式生成器可以生成Cron表达式,同时,也可以反解析。还有一些程序员常用的工具。

四、@Scheduled注解使用建议

4.1.建议 

        在日常使用过程中,除了常用的 cron 表达式以外,还会使用其他几个参数,例如 fixedDelay这些参数,这些参数的单位是毫秒,毫秒(millisecond)是一种较为微小的时间单位,符号为ms。它是秒(second)的千分之一,即1秒等于1000毫秒。

        所以在使用时,假如一个任务执行间隔是 2分钟,那么建议使用下面这种写法: 

@Scheduled(fixedDelay = 1000*60*2)

        这种写法不会提高开发效率,但提高代码的阅读效率 ,可以比较直观的看出任务的时间间隔,而不用换算。执行一次乘法运算本身不会造成毫秒级的时间浪费,乘法运算在计算机中是非常快速的操作,通常可以在微秒级别内完成。

4.2.拓展介绍 

        在Java中,*运算(乘法运算)本身不会直接对系统的运行效率产生显著影响。乘法运算是一种基本的算术运算,它的执行速度非常快,并且对于现代计算机和处理器来说,这种运算的开销是微不足道的。

        然而,系统的运行效率会受到其他因素的影响,例如:

  1. 数据结构:如果你在大型数据集上执行大量的乘法运算,那么数据结构可能会成为性能瓶颈。例如,如果你使用列表或数组来进行乘法运算,那么每次乘法操作都可能需要遍历整个列表或数组,这可能会对性能产生影响。
  2. 算法复杂度:乘法运算的次数与算法的复杂度有关。如果一个算法需要执行大量的乘法运算,那么它的运行时间可能会更长。要减少乘法运算的时间消耗,可以考虑以下方法:

    a)优化算法:通过改进算法或使用更高效的算法来减少乘法运算的次数。

    b)批量处理:如果可能的话,将多个乘法运算合并为一个操作,以减少系统调用的次数和数据传输的开销。

    c)利用硬件特性:根据所使用的硬件,利用其特定的指令集或优化来提高乘法运算的效率。

    d)并行计算:在多核处理器上使用并行计算来加速乘法运算。

  3. 内存和缓存:如果乘法运算涉及大量的内存访问或缓存未命中,那么这可能会对性能产生影响。优化数据布局和缓存使用可以提高内存密集型操作的性能。
  4. 多线程和并发:在多线程环境中,乘法运算可能受到线程同步和竞争条件的影响。合理地使用并发和同步机制可以提高并行计算的性能。
  5. 硬件限制:处理器的架构、内存大小和速度、硬盘速度等硬件因素也会影响系统的运行效率。

五、易错点解释 

5.1 "年"的使用 

        在Spring Boot中,通常使用的cron表达式是基于Quartz Scheduler的语法。Quartz Scheduler的cron表达式由6个字段组成,它们分别表示秒、分、时、日、月、年(可选)。
        在Quartz Scheduler的cron表达式中,年份字段是可选的。当你省略年份字段时,任务会被视为每年都执行。如果你包含年份字段,你可以指定具体的年份,或者使用特殊字符来表示不同的含义。以下是一些在年份字段中可以使用的选项:

        a)具体年份:你可以直接指定一个四位的年份数字,如2023。这意味着任务仅在指定的年份中执行。

        b)问号(?):在年份字段中使用问号表示不指定年份。这通常与月字段中的?一起使用,表示不指定具体的月份,而是让调度器自动计算。

        c)星号(*):星号在年份字段中表示“每一年”。这通常不是很有用,因为即使没有年份字段,任务默认也是每年执行的。但是,在某些情况下,为了保持cron表达式的格式一致性,可能会使用星号。

        d)逗号分隔的列表:你可以在年份字段中使用逗号来分隔多个年份,如2023,2024。这意味着任务将在指定的每个年份中执行。

        e)范围:你可以使用连字符来指定一个年份范围,如2023-2025。这表示任务将在从2023年到2025年(包括两端年份)的每一年中执行。

        f)间隔:你可以使用斜杠来指定年份的间隔,如2023/2。这表示从2023年开始,每隔两年执行一次任务。但是,需要注意的是,Quartz的cron表达式通常不支持在年份字段中使用间隔。

5.2 #的使用 

        # 号左边的 1-7 表示一周的周一到周末,右边的表示第几周。切记,不是0-6。#只允许在周的字段上出现,但实际上SpringBoot的cron表达式中,并没有周这个字段,所以在实际使用中,可以把年这个字段给替换掉。 

         使用 @Scheduled(cron = "0/10 * 17 * * 1#1"),1#1这个字段容易混淆,为了便于理解,我以3#1为例做解释。3#1 是从右往左读的,读作:每月第一周的周三;理解是从左往右理解的,先找每月有周三的周,然后数第一个。以下图日历为例,就是先找周三(7、14、21、28),然后数第一周,也就是7号,其他时间并不会执行任务。

        使用 @Scheduled(cron = "0/10 * 17 * * MON#1"),这是可以直接用星期MON-SUN替换掉 # 左边的 1-7,其他方面和上面的写法一样。

  • 23
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值