通过类上设置注解
@EnableScheduling
可以开启spring自带的定时任务,@Scheduled
设置定时时间。还可以通过ThreadPoolTaskScheduler
的schedule(Runable,cron)
动态添加
使用
@Component
@EnableScheduling //定时任务
public class SchedulingTest{
//每30秒执行一次
@Scheduled(fixedRate = 1000 * 30)
public void doSomeThing(){
//没30秒输出一次
System.out.println ("定时输出:" + dateFormat ().format (new Date ()));
}
//在固定时间执行(当时间匹配规则时输出)
@Scheduled(cron = "0 */1 * * * * ")
public void reportCurrentByCron(){
System.out.println ("固定时间输出" + dateFormat ().format (new Date()));
}
private SimpleDateFormat dateFormat(){
return new SimpleDateFormat ("HH:mm:ss");
}
}
固定时间匹配规则
字段 | 允许值 | 允许的特殊字符 |
---|---|---|
秒 | 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
- 执行方法上配置任务线程池
@Async
//每30秒执行一次
@Async("taskExecutor")
@Scheduled(fixedRate = 1000 * 3)
public void xxx(){
//...
}
分布式情况下避免重复执行
-
lock = redisTemplate.opsForValue().setIfAbsent(KEY, LOCK);
采用Redis
判断是否存在key
,不存在则设置key
,执行完成删除key
的方式加锁(跨时区部署还是会重复执行) -
shedlock
加锁
<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>
配置(jdbc),还有redis,mongo,zookeeper等锁的实现
@Configuration
@EnableScheduling
public class ShedlockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
@Bean
public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
return ScheduledLockConfigurationBuilder
.withLockProvider(lockProvider)
.withPoolSize(10)
.withDefaultLockAtMostFor(Duration.ofMinutes(10))//lock expire最大时间10分钟
.build();
}
}
shedlock表
CREATE TABLE shedlock(
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
加锁
@Scheduled(fixedDelay = 10*1000 /**ms**/,initialDelay = 10*1000)
@SchedulerLock(name = "demoLockTask", lockAtMostFor = 5*1000)
public void checkTask(){
LOGGER.error("checkTask executed");
}
动态添加关闭定时任务
// 引入定时调度线程池
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
// 接收线程调度返回的结果获取类
private ScheduledFuture<?> future;
public void test(){
// 如果已经有任务了就取消原有任务
if (future != null) {
future.cancel(true);
}
// 每月第一天
String cron = "0 0 0 1 * ?";
future = threadPoolTaskScheduler.schedule(new MyRunnable(), new CronTrigger(cron));
// Runnable也可以写成lambda
future = threadPoolTaskScheduler.schedule(()->{
//do..
}, new CronTrigger(cron));
}