@Scheduled // 由Spring定义,用于将方法设置为调度任务。如:定时执行一次或定时轮询执行一段代码。该注解放到需要定时执行的方式上
@EnableScheduling // 再项目启动类上添加注解@EnableScheduling,表示开启定时任务
@Scheduled(fixedDelay=20*1000) //每隔20秒执行
public void scheduledTaskByFixedDelay() {
log.info("定时任务开始 ByFixedDelay:" + DateUtils.getDatetimeString());
String str = "Hello,,,World,,,每20秒,,,指定参数FixedDelay,,,";
List<Map<String,String>> list = helloService.getTemp();
log.info(str+ JSON.toJSONString(list));
log.info("定时任务结束 ByFixedDelay:" + DateUtils.getDatetimeString());
}
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
上一次任务执行完毕时间点之后多长时间再执行,任务的执行要等上一个任务执行结束。
它的间隔时间是根据上次的任务结束的时候开始计时的。
比如一个方法上设置了 fixedDelay=10*1000,那么当该方法某一次执行结束后,开始计算时间,当时间达到10秒,就开始再次执行该方法。
@Scheduled(fixedDelay=10000) //每隔10秒执行
@Scheduled(fixedDelay=60*60*1000) //每隔1小时执行
上一次任务开始执行点之后指定时间后再执行(如果执行时间超过了间隔时间,那上一次任务执行完之后,立即执行下一个任务)
fixedRate 理解起来比较麻烦,它的间隔时间是根据上次任务开始的时候计时的。比如当方法上设置了 fiexdRate=5000,该执行该方法所花的时间是2秒,那么3秒后就会再次执行该方法。
但是这里有个坑,当任务执行时长超过设置的间隔时长,那会是什么结果呢。打个比方,比如一个任务本来只需要花2秒就能执行完成,我所设置的fixedRate=5000,但是因为网络问题导致这个任务花了7秒才执行完成。当任务开始时Spring就会给这个任务计时,5秒钟时候Spring就会再次调用这个任务,可是发现原来的任务还在执行,这个时候第二个任务就阻塞了(这里只考虑单线程的情况下,多线程后面再讲),甚至如果第一个任务花费的时间过长,还可能会使第三第四个任务被阻塞。被阻塞的任务就像排队的人一样,一旦前一个任务没了,它就立马执行。
@Scheduled(fixedRate=60000) //任务开始执行后隔60秒执行
@Scheduled(fixedRate=60*60*1000) //任务开始执行后隔1小时执行
第一次任务将会延迟指定时间后启动
@Scheduled(fixedDelay=60*60*1000,initialDelay=60*1000) //第一次任务延迟1分钟启动,之后每隔1小时执行
该参数接受一个cron表达式,cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义。
@Scheduled(cron="秒 分 时 日 月 周 年") // 年不是必需的,可省略
@Scheduled(cron="秒 分 时 日 月 周 年") // 年不是必需的,可省略
其中,每个时间字段都有对应的取值范围和特殊符号。下面是每个时间字段的详细说明:
1、秒 (Seconds):取值范围为 0~59。例如,'0/5'表示每隔5秒触发一次。'*'表示每秒种都触发。允许使用特殊字符 , - * /
2、分钟 (Minutes):取值范围为 0~59。例如,'0/5'表示每隔5分钟触发一次。'*'表示每分种都触发。允许使用特殊字符 , - * /
3、小时 (Hours):取值范围为 0~23。例如,'0/2'表示每隔2小时触发一次。'*'表示每小时都触发。允许使用特殊字符 , - * /
4、日期 (Day of Month):取值范围为 1~31。例如,'1,15'表示每月的1日和15日触发,'*'表示每天都触发。允许使用特殊字符 , - * ? / L W C
5、月份(Month):取值范围为 1~12,也可以使用英文缩写JAN、FEB、MAR等。例如,'1,6'表示一月和六月触发,'*'表示每个月都触发。允许使用特殊字符 , - * /
6、星期 (Day of Week) :取值范围为 1~7,1 表示星期日,2 表示星期一,以此类推,也可以使用英文缩写 SUN、MON、TUE 等。例如,'2-6'表示星期一到星期五触发,'*' 表示每个星期都触发。允许使用特殊字符 , - * ? / L C #
7、年份 (Year):可选字段,表示触发条件的年份。例如,'2023' 表示在2023年触发,'*' 表示每年都触发。允许使用特殊字符 , - * /
// 在使用时,由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?
除了取值范围,Cron 表达式还支持一些特殊符号,用于指定特定的触发条件,例如:
星号(*):代表所有可能的取值,表示不限制该时间字段的取值范围。
问号(?):仅在日期和星期字段中使用,表示不指定具体的取值,可以任意匹配。
斜线(/):表示间隔触发,例如在分钟字段中,"*/5"表示每隔5分钟触发一次。"0/15"表示从第0分钟开始,每15分钟。"3/20"表示从第3分钟开始,每20分钟(它和"3,23,43")的含义一样
逗号(,):用于指定多个取值,例如在小时字段中,"1,3,5" 表示在第 1、3、5 小时触发。
减号(-):用于指定一个范围,例如在月份字段中,"3-6" 表示三月到六月触发。
"L" 字符仅被用于天(月)和天(星期)两个子表达式,它是单词"last"的缩写
如果在"L"前有具体的内容,它就具有其他的含义了。例如:"6L"表示这个月的倒数第6天
注意:在使用"L"参数时,不要指定列表或范围,因为这会导致问题
"W" 字符代表着工作日(Mon-Fri),并且仅能用于日历中。它用来指定离指定日的最近的一个星期日。大部分的商业处理都是基于工作周的,所以 W 字符可能是非常重要的。
例如,日域中的 15W 意味着 "离该月15号的最近一个工作日"。 假如15号是星期六,那么 trigger 会在14号(星期五)触发,因为星期伍比星期一离15号更近。
"C" 字符代表"Calendar"的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
"0 0 10,14,16 * * ?" 每天上午10,14,16点
"0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时
"0 0 12 ? * WED" 表示每个星期三中午12点
"0 0 12 * * ?" 每天中午12点触发
每天上午10:15触发: "0 15 10 ? * *" "0 15 10 * * ?" "0 15 10 * * ? *"
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"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触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
@Scheduled(cron = "*/5 * * * * ?") \\每隔5秒钟执行一次
@Scheduled(cron = "0 */1 * * * ?") \\每隔一分钟执行一次
@Scheduled(cron = "0 26,29,33 * * * ?") \\在各个小时的26分、29分、33分执行一次
@Scheduled(cron = "0 0 */2 * * ?") \\每隔两小时执行一次
@Scheduled(cron = "0 0 1 * * ?") \\每天凌晨1点执行一次
@Scheduled(cron = "0 0 6,15,20 * * ?") \\每天的6点、15点、20点触发定时任务
@Scheduled(cron = "0 0 2 1 * ?") \\每月1号凌晨2点执行一次
@Scheduled(cron = "0 0 23 L * ?") \\每月最后一天23点执行一次
@Scheduled(cron = "0 0 3 ? * L") \\每周星期六凌晨3点执行一次
@Scheduled(cron = "0 0 12 ? * SUN") \\每周星期天中午12点执行一次
@Scheduled(cron = "0 0 0 L * ?") \\每个月的最后一天执行一次
@Scheduled(cron = "0 0/5 9-17 * * MON-FRI") \\工作日的早上 9 点到下午 5 点,每隔 5 分钟触发定时任务
Scheduled(cron=“0 */1 * * * ?”)
每一分钟执行一次。比如第一次执行时间为2023-10-20 23:08:12,下一次2023-10-20 23:09:12,再下一次2023-10-20 23:10:12
@Scheduled(cron = “10 * * * * ?”)
每分钟的第十秒执行一次。比如第一次执行时间为2023-10-20 23:09:10,下一次2023-10-20 23:10:10,再下一次2023-10-20 23:11:10。 之所以用(23:09:10)举例,是因为上一个任务是在(23:08:12)才启动,所以任务只能从23:09:10开始
@Scheduled(cron = “*/10 * * * * ?”)
每十秒钟执行一次。比如第一次执行时间为2023-10-20 23:08:12,下一次2023-10-20 23:08:22,再下一次2023-10-20 23:08:32
@Scheduled(initialDelay = 5, fixedDelayString = "${erwin.fixed-delay:2}", timeUnit = TimeUnit.SECONDS)
public void scheduledTaskByFixedDelay() {
log.info("定时任务开始 ByFixedDelay:" + DateUtils.getDatetimeString());
log.info("Hello,,,World,,,指定参数fixedDelay,,,");
}
配置文件 .yml
erwin:
fixed-delay: 5
如果在配置文件中没有配置,则每隔2秒执行一次,如果配置了,就每隔5秒执行一次。initialDelay 表示,项目启动后,5秒开始执行第一次任务。
注意:${erwin.fixed-delay:2},冒号前后不能有空格。
@Scheduled(initialDelay = 5, fixedRateString = "${erwin.fixed-rate:2}", timeUnit = TimeUnit.SECONDS)
public void scheduledTaskByFixedRate() {
log.info("定时任务开始 ByFixedRate:" + DateUtils.getDatetimeString());
log.info("Hello,,,World,,,指定参数fixedRate,,,");
}
配置文件 .yml
erwin:
fixed-rate: 5
如果配置文件没有配,就会使用默认的值。
@Scheduled(cron = "${erwin.cron:0/10 * * * * ?}")
public void scheduledTaskByCron() {
log.info("定时任务开始 ByCron:" + DateUtils.getDatetimeString());
log.info("Hello,,,World,,,指定参数cron,,,");
}
配置文件 .yml
erwin:
cron: 0/30 * * * * ?