在 JDK 中,内置了两个类,可以实现定时任务的功能:
但是它们仅支持按照指定频率,不直接支持指定时间的定时调度,需要结合Calendar自行计算,才能实现复杂时间的调度。
它们是进程级别,而我们为了实现定时任务的高可用,需要部署多个进程。此时需要多考虑,多个进程下,同一个任务在相同时刻,不能重复执行
项目可能存在定时任务较多,需要统一的管理,此时不得不进行二次封装
所以我们可以用调度任务中间件
在 Spring 体系中,内置了两种定时任务的解决方案:
// ScheduleConfiguration.java
@Configuration
@EnableScheduling
public class ScheduleConfiguration {
}
在类上,通过添加 @EnableScheduling 注解,启动 Spring Task 的定时任务调度的功能。
@Component
public class TaskDemo {
private Logger logger = LoggerFactory.getLogger(getClass());
private final AtomicInteger counts = new AtomicInteger();
@Scheduled(fixedRate = 2000)
public void execute() {
logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet());
}
}
其中的 #execute() 方法用于实现任务要执行的内容,这里是打印一下日志。
同时,在该方法上,添加 @Scheduled 注解,设置每 2 秒执行该方法。
@Configuration
@EnableScheduling//开启定时任务
@EnableAsync//开启多线程
pubic class config{
@Scheduled(cron="0 0 23 * * ?")//每天23点执行
@Async//异步
public void method(){}
}
@Scheduled常用属性
@Scheduled 注解用于设置定时任务的执行计划。
常用属性如下:
注意三者区别。
不常用属性如下:
Spring Task配置
在 application.yml 中,添加 Spring Task 定时任务的配置,如下:
spring:
task:
# Spring Task 调度任务的配置,对应 TaskSchedulingProperties 配置类
scheduling:
thread-name-prefix: sh-demo- # 线程池的线程名的前缀。默认为 scheduling- ,建议根据自己应用来设置
pool:
size: 10 # 线程池大小。默认为 1 ,根据自己应用来设置
shutdown:
await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置
注意,spring.task.scheduling.shutdown 配置项,是为了实现 Spring Task 定时任务的优雅关闭。
我们想象一下,如果定时任务在执行的过程中,如果应用开始关闭,把定时任务需要使用到的 Spring Bean 进行销毁
例如说数据库连接池,那么此时定时任务还在执行中,一旦需要访问数据库,可能会导致报错。
在 Quartz 体系结构中,有三个组件非常重要,它们是 Job,Trigger 和 Scheduler:
执行任务类继承Job,实现execute方法
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
.withIdentity("job1","group1")//任务名
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("tigger1","tiggerGroup1")
.startNow()//立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)//每隔1s执行一次
.repeatForever())//一直执行
.build();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
创建Job
public class HelloQuartz implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("Hello Quartz!");
}
}
调用Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class HelloQuartzApplication {
public static void main(String[] args) throws SchedulerException, InterruptedException {
// 1. 通过 SchedulerFactory 获取一个调度器
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = stdSchedulerFactory.getScheduler();
// 2. 创建jobDetail实例,绑定Job实现类
JobDetail job = JobBuilder.newJob(HelloQuartz.class).withIdentity("HelloJob", "HelloJobGroup").build();
// 3. 创建触发器
// // SimpleTrggier,定义调度触发规则
// SimpleTrigger trigger1 = TriggerBuilder
// .newTrigger().withIdentity("HelloSimpleTrigger", "HelloSimpleTriggerGroup")
// .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3).withRepeatCount(6))
// .startNow().build();
// // 把作业和触发器注册到任务调度中
// scheduler.scheduleJob(job, trigger1);
// CronTrigger,corn表达式:每五秒执行一次
Trigger trigger2 =TriggerBuilder.newTrigger().withIdentity("HelloCronTrigger", "HelloCronTriggerGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?"))
.startNow().build();
scheduler.scheduleJob(job, trigger2);
// 4. 启动调度
scheduler.start();
Thread.sleep(18000);
// 5. 停止调度
scheduler.shutdown();
}
}