前言
本文主要介绍两种定时任务的实现方式,一种是Spring内置的定时任务Spring Scheduler,还有一种是Spring Boot集成的Quartz。第一种方式应用起来比较简单,支持cron表达式和固定频率,但是无法监控任务状态,不支持动态调整,而Quartz框架是一个功能更为强大的调度器。
Spring Scheduler
Spring自带的定时任务有两种实现方式基于注解和基于接口,这两种形式都需要在启动类加上@EnableScheduling开启配置。
第一种:@Scheduled注解式
/**
* cron表达式语法:[秒] [分] [小时] [日] [月] [周] [年]
* @Scheduled(fixedDelay = 5000) //上一次执行完毕时间点之后5秒再执行
* @Scheduled(fixedDelayString = "5000") //上一次执行完毕时间点之后5秒再执行
* @Scheduled(fixedRate = 5000) //上一次开始执行时间点之后5秒再执行
* @Scheduled(initialDelay=1000, fixedRate=5000) //第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
* @Schedules 可以使用这个注解来指定多个@Scheduled规则
*/
@Component
public class DemoScheduled {
@Scheduled(cron = "*/10 * * * * ?")
public void doCron() {
System.out.println(LocalDateTime.now() + "根据cron表达式执行定时任务");
}
@Scheduled(fixedRate = 1000 * 10)
public void doRate() {
System.out.println(LocalDateTime.now() + "根据上一次任务开始以后多久时间执行定时任务");
}
@Scheduled(fixedDelay = 1000 * 10)
public void doDelay() {
System.out.println(LocalDateTime.now() + "根据上一次任务结束以后多久时间执行定时任务");
}
@Scheduled(initialDelay = 1000 * 10, fixedDelay = 1000 * 10)
public void doInitial() {
System.out.println(LocalDateTime.now() + "首次延迟多长时间后执行");
}
@Schedules(value = {@Scheduled(cron = "*/20 * * * * ?"), @Scheduled(fixedDelay = 1000 * 15)})
public void schedules() {
System.out.println(LocalDateTime.now() + "多个定时配置");
}
}
第二章基于SchedulingConfigurer接口实现
@Component
public class DemoSchedulingConfigurer implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 等同于@Scheduled(cron = "*/10 * * * * ?")
taskRegistrar.addCronTask(()-> System.out.println(LocalDateTime.now() + "根据cron表达式执行定时任务"),
"*/10 * * * * ?");
// 等同于@Scheduled(fixedRate = 1000 * 10)
taskRegistrar.addFixedRateTask(() -> System.out.println(LocalDateTime.now() + "根据上一次任务开始以后多久时间执行定时任务"),
1000 * 10);
// 等同于@Scheduled(fixedDelay = 1000 * 10)
taskRegistrar.addFixedDelayTask(() -> System.out.println(LocalDateTime.now() + "根据上一次任务结束以后多久时间执行定时任务"),
1000 * 10);
}
}
这两种方式都是默认串行单线程执行,任务的执行时间会收到上一次任务执行时间的影响,如果要多线程执行需要单独配置线程池。
/**
* 自定义线程池
*/
@Configuration
public class ThreadPoolConfig {
@Bean("executor")
public Executor getExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10,
20,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue(10000));
return executor;
}
}
基于注解多线程,通过@Async来实现多线程,需要在启动类加上@EnableAsync
@Scheduled(cron = "* * * * * ?")
@Async("executor")
public void doCronByPool() {
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行1");
}
@Scheduled(fixedRate = 1000 * 1)
@Async("executor")
public void doRateByPool() {
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行2");
}
基于接口多线程,通过ScheduledTaskRegistrar的setTaskScheduler来设置
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 添加线程池
taskRegistrar.setTaskScheduler(getTaskScheduler());
taskRegistrar.addCronTask(() -> System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行1"),
"* * * * * ?");
taskRegistrar.addFixedRateTask(() -> System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行2"),
1000 * 1);
}
private ThreadPoolTaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);
taskScheduler.initialize();
return taskScheduler;
}
Spring Boot 整合 Quartz
spring boot项目中可以直接引入starter-quartz
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
先创建一个任务类用于任务调度后具体处理操作
/**
* 继承spring封装的QuartzJobBean
*/
public class DemoJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println(LocalDateTime.now() + "DemoJob");
}
}
然后写一个配置类将任务类与触发时间绑定
/**
* Quartz的相关配置,注册JobDetail和Trigger
* 注意JobDetail和Trigger是org.quartz包下的,不是spring包下的,不要导入错误
*/
@Configuration
public class DemoJobConfig {
@Bean
public JobDetail demoJobDetail() {
JobDetail jobDetail = JobBuilder.newJob(DemoJob.class)
.withIdentity("demoJob")
.storeDurably()
.build();
return jobDetail;
}
@Bean
public Trigger demoTrigger() {
// 间隔10秒
SimpleScheduleBuilder simpleSchedule = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever();
// cron表达式
// CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("*/10 * * * * ?");
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(demoJobDetail())
.withIdentity("demoJob")
.withSchedule(simpleSchedule)
.startNow()
.build();
return trigger;
}
}
参考文档
https://docs.spring.io/spring-boot/docs/2.6.3/reference/html/io.html#io.quartz