文章字数:675,阅读全文大约需要2分钟
通过类上设置注解
@EnableScheduling可以开启spring自带的定时任务,@Scheduled设置定时时间。还可以通过ThreadPoolTaskScheduler的schedule(Runable,cron)动态添加
使用
1 |
|
固定时间匹配规则
| 字段 | 允许值 | 允许的特殊字符 |
|---|---|---|
| 秒 | 0-59 | , - * / |
| 分 | 0-59 | , - * / |
| 小时 | 0-23 | , - * / |
| 日期 | 1-31 | , - * ? / L W C |
| 月份 | 1-12 或者 JAN-DEC | , - * / |
| 星期 | 1-7 或者 SUN-SAT | , - * ? / L C # |
| 年 | 空, 1970-2099 | , - * / |
- 每个元素都可以是一个值如
6,一个区间9-12一个间隔时间8-18/4/4代表间隔四个小时,一个列表1,3,5 - 日期和星期互斥,即两个元素重合,必须其中一个设置
?忽略 *代表所有可能的值/指定数值的增量,如0/10(分钟单位中)代表0分钟开始,10分钟执行一次?仅在日期和星期中,代表不指定值L用于日期和星期中,代表倒数第几个W仅在日其中,代表平日(工作日)。15W代表离15号最近的一个工作日。C日期,5C五个单位后的第一天#每个月第几个星期几,例如在4#2表示某月的第二个星期三。
转换异步线程
单线程执行时间超过定时间隔可能会出现任务丢失的情况,可以使用异步线程避免这个问题。
- 配置
Spring的@EnableAsync - 执行方法上配置任务线程池
@Async1
2
3
4
5
6//每30秒执行一次
("taskExecutor")
(fixedRate = 1000 * 3)
public void xxx(){
//...
}
分布式情况下避免重复执行
lock = redisTemplate.opsForValue().setIfAbsent(KEY, LOCK);采用Redis判断是否存在key,不存在则设置key,执行完成删除key的方式加锁(跨时区部署还是会重复执行)shedlock加锁配置(jdbc),还有redis,mongo,zookeeper等锁的实现1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>0.16.1</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>0.16.1</version>
</dependency>shedlock表1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ShedlockConfig {
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder
.withLockProvider(lockProvider)
.withPoolSize(10)
.withDefaultLockAtMostFor(Duration.ofMinutes(10))//lock expire最大时间10分钟
.build();
}
}加锁1
2
3
4
5
6
7CREATE TABLE shedlock(
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)1
2
3
4
5(fixedDelay = 10*1000 /**ms**/,initialDelay = 10*1000)
(name = "demoLockTask", lockAtMostFor = 5*1000)
public void checkTask(){
LOGGER.error("checkTask executed");
}
动态添加关闭定时任务
1 | // 引入定时调度线程池 |