Spring 配置 Quartz 定时器动态创建任务在修改任务后出现立即重复执行的情况
前言
在工作中有个专门去跑定时任务的小项目,它负责读取数据库配置的定时任务,然后生成相应的定时任务job进行执行,每1分钟读取一次,如果数据库中的定时任务有更新,则会重新加载执行。
问题描述
有一次需要将某个定时任务执行多次,于是想通过更改该定时任务的cron表达式,执行了一次后再增加时间修改cron表达式让它再次执行,但实践后发现定时任务在更改后程序读取到任务配置更新了就立即执行了,等到达设定时间后又再次执行了一次,相当于更新后执行了2次,但我们想要的是让它按照我们设定的时间执行,而不是更新后立即执行。
分析与解决方案
MisfireInstruction
trigger中有一个MisfireInstruction的属性,这个属性是用来控制定时器错过时间后是否还要继续执行。
misfireInstruction的数据类型为int
MISFIRE_INSTRUCTION_SMART_POLICY = 0
misfireInstruction的默认值就是该值
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1
错过任务后立即执行1次
MISFIRE_INSTRUCTION_DO_NOTHING = 2
错过任务后不执行已错过的任务,只执行当前的
上述遇到的问题就是这个属性导致的,因为默认情况下,定时器错过执行时间后会立即补救执行一次上一次没执行的任务,然后再执行当前这一次时间的任务,所以上面的问题才会执行2次,因为修改cron表达式后,定时器发现上一次没有执行,于是就立即执行了一遍,然后再执行当前这次的任务。
解决方法
方法1
由于我只是想要执行完定时任务后再修改时间让它再次执行,为了不再被定时器发现上一次没有执行,可以加上当前的日期。
cron表达式修改前
0 20 14 * * ?
cron表达式修改后
0 20 14 24 4 ? 2022
这样精确设置时候后就不会在定时任务修改后出现立即执行的情况了
方法2
修改msifireinstruction值,修改为MISFIRE_INSTRUCTION_DO_NOTHING=2
c.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
示例代码
启动
执行MainStart类下的main方法
问题复现
启动执行5分钟后,修改cron.txt文件的cron表达式,改为启动时间到当前时间这五分钟内的时间,等待程序检测到任务更新后即可复现
例如:MainStart启动时间为10:20,五分钟后就是10:25,然后修改cron表达式为0 22 10 * * ?
即可发现在更新任务后立即执行了任务一次
示例代码:https://gitee.com/welitis/blog_code/tree/master/SpringQuartzRepeatExe
参考
Quartz框架(四)—misfire处理机制 - 简书 (jianshu.com)