SpringBoot 中使用 Schedule 定时任务非常简单,首先我们创建一个 SpringBoot 项目,在 application 启动类加上 @EnableScheduling 注解,如下:
@SpringBootApplication
@EnableScheduling
public class ScheduledemoApplication {
public static void main(String[] args) {
SpringApplication.run(ScheduledemoApplication.class, args);
}
}
接着,我们写个简单的定时任务类,在类上添加 @Scheduled 注解即可,如下:
@Component
public class ScheduleTest {
@Scheduled(cron = "0/3 ?") // 表示隔3秒执行一次
public void say() {
System.out.println("定时任务开始执行:say hello!");
}
}
当我们启动项目时,定时任务就会自己开启。
@Scheduled 注解源码如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String CRON_DISABLED = "-";
String cron() default ""; // 设置 cron 表达式
String zone() default ""; // 设置时区
long fixedDelay() default -1L; // 固定间隔任务,上一次任务完成后多久执行下次任务,单位毫秒
String fixedDelayString() default ""; // 同上,时间值是 String 类型
long fixedRate() default -1L; // 固定频率任务,上次任务开始后多久执行下次任务,单位毫秒
String fixedRateString() default ""; // 同上,时间值是 String 类型
long initialDelay() default -1L; // 任务首次执行延迟的时间,单位毫秒
String initialDelayString() default ""; // 同上,时间值是 String 类型
}
cron - 表达式的使用
cron 表达式是一个字符串,字符串以5个或6个空格隔开,分为6或7个域,每一个域代表一个含义,其格式如下:
- Seconds Minutes Hours DayofMonth Month DayofWeek Year(非必须)
- 秒 分 时 月内日 月 周内日 年
各个格式允许的值和允许的特殊字符如下:
字段 | 允许的值 | 允许的特殊字符 |
---|---|---|
秒 | 0-59 | , - / |
分 | 0-59 | , - / |
小时 | 0-23 | , - / |
月内日期 | 1-31 | , - / ? L W C |
月 | 1-12 或 JAN - DEC | , - / |
周内日期 | 1-7 或 SUN - SAT | , - / ? L C # |
年(非必须) | 留空 或 1970-2099 | , - / |
特殊字符含义:
特殊字符 | 意义 |
---|---|
匹配所有值,假如在 Minutes 域使用 ,即表示每分钟都会触发事件 | |
? | 只能在日期域和星期域中使用,用来指定"非明确的值",因为它们之间相互影响,其中一个用了另外一个只能用 ? |
- | 指定一个范围,例如"1-3"在 Minutes 域中表示冲1-3分钟内每分钟触发一次 |
/ | 指定增量,表示每间隔多长时间触发,如:“0/15"在 Seconds 域表示每分钟的0、15、30和45秒,“5/15"在 Minutes 域中表示每小时的5、20、35、50分钟。”/10"等价于"0/10” |
, | 指定可选值,"5,10"在 Minutes 域中表示在一个小时中第5分钟和第10分钟 |
L | 只能在日期域和星期域中使用,在日期域表示为最后一天,但星期域中可以在L前加个数字,如"5L",表示一个月最后一个星期四 |
W | 只能出现在日期域,表示指定日期的最近工作日,不能跨月份只能往后顺延,且只能与单独数字使用 |
LW | 可在日期域中联合使用,表示这个月的最后一个工作日 |
# | 只能在星期域中使用,表示本月的第几周的星期几,如:"6#3"表示第三周的星期五 |
C | 这个没搞懂… |
zone - 时区设置
我们可以通过 TimeZone
类 的getDefault()
查看当前系统所在的时区,如下:
System.out.println(TimeZone.getDefault());
// 打印结果为:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
其中的 id值为我们可以在 @Scheduled 注解中使用,我们可以通过 ZoneId 类的 getAvailableZoneIds() 方法获取所有的时区值:
System.out.println(ZoneId.getAvailableZoneIds()); // 值有很多,可自己打印查看
fixedDelay && fixedDelayString - 固定间隔任务
下一次的任务执行时间,是从方法最后一次任务执行结束时间开始计算。并以此规则开始周期性的执行任务。fixedDelayString是使用String类型参数
比如 a 方法 设置 fixedDelay = 1000 * 10
,在第 0 秒开始执行,方法本身执行时间为 12 秒,那么下一次的执行时间为第22 秒。
fixedRate && fixedRateString- 固定频率任务
按照指定频率执行任务,并以此规则开始周期性的执行调度。fixedRateString使用String类型参数
当然一般情况下我们定时任务的cron
等值是放在properties文件中,我们可以使用“${}”来获取
spring中有一套默认的配置,使得即使在我们没有指定调度器的情况下也可以正常工作起来;默认的调度配置使用单线程调度,所有的任务都必须等待当前任务执行完成才可以继续执行。如果只有一个调度任务,且调度任务的时间比调度时间短,那么问题不大;如果有几个调度任务一起执行,那么单线程的调度任务就不满足我们的需求,因此我们要自定义调度线程池来保证我们需求,如下:
@Configuration
public class SchedulePoolConfiguration {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler schedulerPool = new ThreadPoolTaskScheduler();
// 设置调度任务线程池大小
schedulerPool.setPoolSize(3);
return schedulerPool;
}
}