这两天发现生产上某些定时任务没有正常执行,查了下发现 Spring Boot 使用 @Scheduled 执行定时任务是在单线程中,由于数据量增长过大,导致定时任务执行时间过长,导致其他后续任务阻塞、卡死,导致其它任务没有执行。

单线程

可以看到,默认的线程池大小为1,所以定时任务是单线程执行的。

  • org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
  • org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
1
2
3
4
5
6
7
8
9
10
11
12
@ConfigurationProperties("spring.task.scheduling")
public class TaskSchedulingProperties {
...
public static class Pool {

/**
* Maximum allowed number of threads.
*/
private int size = 1;
}
...
}

优化方案

1. 调整线程池大小

多线程执行,两个不同任务无需互相等待,但是同一个任务仍然需要等待。

1
2
3
4
5
6
7
# fixed: The scheduled task is not executed
spring:
task:
scheduling:
pool:
size: 10
thread-name-prefix: mall-scheduling-

2. 指定线程池,异步执行

多线程执行,同一个任务也无需等待。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class ThreadPoolConfig {
@Bean("mallTaskExecutor")
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("mall-async-executor-");

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

@Async("mallTaskExecutor")
@Scheduled(cron = "30 0 0 * * ? ")
@SchedulerLock(name = "dayStats", lockAtMostFor = "86400000", lockAtLeastFor = "82800000")
public void dayStats() {
log.info("开始执行日统计任务");
...
}