概述
SpringBoot定时任务常用方案:Schedule、Quartz。Schedule属于spring框架内置的功能,所属资源包spring-context-support;开发简单、执行效率高,多任务场景需要结合线程池(线程池大小需要谨慎设置)共同使用,以避免出现阻塞、崩溃、延迟启动等现象。Quartz纯Java开源调度框架,集群模式通过故障转移和负载平衡功能提供高可用性和可扩展性。本文主要探究Schedule,暂不考虑Quartz。Schedule以表达式加载方式维度可以分为:静态表达式、动态表达式,以执行模式维度可以分为:单线程、多线程。

静态表达式单线程
设置两个定时任务,通过sleep模拟task1耗时10s
//不受上次执行时间点影响,每隔5秒再执行。
@Scheduled(cron = "0/5 * * * * *")
private void cronTask1() {
logger.info("task1"+Thread.currentThread().getName()+""+newDate().toString());
//模拟定时任务卡死
try {
Thread.sleep(10000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Scheduled(cron="0/5*****")
privatevoidcronTask2(){
logger.info("task2"+Thread.currentThread().getName()+""+newDate().toString());
}

如上图所示,本来预期效果是要两个定时任务都是每5s执行,实际效果是都是15s(10s的耗时)。默认情况下,Schedule调度任务使用单一线程串行执行。如果只有一个任务,这样做没问题。如果多个任务,一旦有任务耗时较长或者卡死,会直接导致其他任务延迟或者卡死。这种情况,就需要启用多线程。
静态表达式多线程
定义线程池
@Configuration
@EnableAsync //开启线程池
public class AsyncConfigurer2 implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("AsyncConfigurer2-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
定时任务启用线程池
@Async //使用线程池
@Scheduled(cron = "* * * * * *")
public void task() {
logger.info(Thread.currentThread().getName() + " " + new Date().toString());
//模拟定时任务卡死
try {
Thread.sleep(5000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
如果还是使用单线程,本来定时每秒调度间隔,由于模拟任务卡死耗时5秒,调度间隔会成为6秒。启用多线程,会达到预期的调度间隔每秒的效果,如下图:

完整代码:https://github.com/felixzh2020/felixzh-learning-springboot