@Scheduled注解
@Scheduled注解是spring boot提供的用于定时任务控制的注解,主要用于控制任务在某个指定时间执行,或者每隔一段时间执行.
配置@Scheduled主要有三种配置执行时间的方式(cron/fixedRate/fixedDelay)。它的配置一共有8个参数,需要配合@EnableScheduling注解使用(在启动类上,声明 @EnableScheduling,表示开启定时任务;或者在定时任务的每个类上都加上@EnableScheduling注解)。
cron表达式
该参数接收一个cron表达式,cron表达式是一个字符串,以5或6个空格隔开,分开共6或7个域,每一个域代表一个含义。
cron 表达式在线生成地址:https://cron.qqe2.com/
语法
[秒] [分] [小时] [日] [月] [周] [年]
常用通配符说明*:
表示所有值。 例如:在分的字段上设置 *,表示每一分钟都会触发。?:
表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为“?”, 具体设置为 0 0 0 10 * ?
例如:
@Scheduled(cron = "0 0 1 * * ?")
// 每天凌晨一点各执行一次(使用的时候直接放在定时任务方法上即可)
fixedRate
该配置表示上一次执行时间后距离多长时间再执行;
例如:
@Scheduled(fixedRate = 5000) //上一次开始执行时间点之后5秒再执行
@Scheduled(fixedRate = 1000 * 10) //10秒发送一次
与cron表达式区别:该配置在实际使用中(本地配置后),重启tomcat即可生效;而cron表达式只能等到约定时间后才能生效;
fixedDelay
上一次执行完毕时间点之后多长时间再执行。
@Scheduled(fixedDelay = 5000)
上面介绍了,几种参数,下面记录一下我在项目中遇到的问题
场景案例:
新项目中,有个天气需要每个一个小时更新一下,调用外部接口。这里我准备用@Scheduled定时任务。
但是想到一个问题:外部接口不稳定,存在一种情况线程任务启动了,一直在执行中,长时间不结束,定时任务周期到了,由于@Scheduled是单线程,导致数据没有得到及时更新;同时,假如存在多个定时任务,有可能导致其他任务不能及时执行
解决方案:
方案一:使用@Async注解实现异步任务
方案二:配置线程池
我选用,方案二,配置线程池代码,这里实现方式可以是多样的。这里做了一个全局配置,利用线程池,去支撑单线程定时任务
/**
* 配置线程池
* 防止定时任务 @Scheduled 单线程阻塞
*/
@Configuration
public class ScheduledConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
两种方式各有优缺点:
比较 | 方案一 | 方案二 |
---|---|---|
优点 | 注解方式使用简单,代码量少 | 配置灵活,线程数可控 |
缺点 | 线程数不可控,可能存在资源浪费 | 需要增加编码 |
补充:
定时任务调度框架:Timer,ScheduledExecutorService,SpringTask,Quartz,xxl-job,elastic-job