基于springboot实现定时任务
springboot 框架本身的定时任务比较简单,在启动类中使用 @EnableScheduling 注解开启定时任务,会自动扫描,相当于一个开关,把这个开关开完之后,那么只要在相应的任务类中做相应的任务,那么就会被 spring boot 容器扫描到,扫描到后,根据任务定义的时间会自动运行。
先搭建好Spring Boot微服务,加上这个注解 @EnableScheduling :
@SpringBootApplication
@EnableCaching // 启用缓存功能
@EnableScheduling // 开启定时任务功能
public class SpringBootTaskApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTaskApplication.class, args);
}
}
@Scheduled参数说明:
- cron:cron表达式,指定任务在特定时间执行;
- fixedDelay:表示上一次任务执行完成后多久再次执行,参数类型为long,单位ms;
- fixedDelayString:与fixedDelay含义一样,只是参数类型变为String;
- fixedRate:表示按一定的频率执行任务,参数类型为long,单位ms;
- fixedRateString: 与fixedRate的含义一样,只是将参数类型变为String;
- initialDelay:表示延迟多久再第一次执行任务,参数类型为long,单位ms;
- initialDelayString:与initialDelay的含义一样,只是将参数类型变为String;
- zone:时区,默认为当前时区,一般没有用到。
Cron表达式参数分别表示:
- 秒(0~59) 例如0/5表示每5秒
- 分(0~59)
- 时(0~23)
- 日(0~31)的某天,需计算
- 月(0~11)
- 周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
在线cron表达式生成:http://qqe2.com/cron/index
1、单线程执行
/**
* scheduler定时器执行任务的类
*/
@Component
public class PrintTask {
private Logger logger = LoggerFactory.getLogger(getClass());
private int i;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
/**
* 注解表明这是一个需要定时执行的方法,
* 里面的cron属性接收的是一个cron表达式,这里的意思是每隔15秒执行一次方法
*/
@Scheduled(cron = "*/15 * * * * ?")
public void execute() {
logger.info("thread id:{},PrintTask execute times:{}", Thread.currentThread().getId(), ++i);
}
/**
* 每5s执行一次
*/
@Scheduled(fixedRate = 5000)
public void taskOne(){
logger.info("定时任务1执行!!!执行时间:{}",dateFormat.format(new Date()));
}
}
缺点: 使同一个线程中串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行。
2、多线程执行
在SpringBoot项目中使用config配置类的方式添加多线程异步配置,然后在定时任务的类或者方法上添加@Async ,最后重启项目,每一个任务就会在不同的线程中。新建一个AsyncConfig类:
}
@Configuration //表明该类是一个配置类
@EnableAsync //开启异步事件的支持
@RefreshScope //动态刷新配置
@ConfigurationProperties(prefix = "taskthread.config")
public class AsyncConfig {
private int corePoolSize = 10;
private int maxPoolSize = 200;
private int queueCapacity = 10;
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.initialize();
return executor;
}
}
任务类加上异步注解:
@Component
public class PrintTask {
private Logger logger = LoggerFactory.getLogger(getClass());
private int i;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
/**
* 注解表明这是一个需要定时执行的方法,
* 里面的cron属性接收的是一个cron表达式,这里的意思是每隔15秒执行一次方法
*/
@Async
@Scheduled(cron = "*/15 * * * * ?")
public void execute() {
logger.info("thread id:{},PrintTask execute times:{}", Thread.currentThread().getId(), ++i);
}
/**
* 每5s执行一次
*/
@Async
@Scheduled(fixedRate = 5000)
public void taskOne(){
logger.info("定时任务1执行!!!执行时间:{}",dateFormat.format(new Date()));
}
}
注意点: 第一个定时任务和第二个定时任务互不影响;并且,由于开启了多线程,第一个任务的执行时间也不受其本身执行时间的限制,所以需要注意可能会出现重复操作导致数据异常。
缺点:
- 1、执行周期写死在代码里了,没有办法动态改变,要想改变只能修改代码在重新部署启动微服务
- 2、分布式应用中多个实例的定时任务是会同时执行的,这样不仅会消耗资源,而且可能还会引起数据库锁。