问题描述:
2022-08-18 01:59:00.001 INFO [irms-transfer-service,,,] 15 --- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务开启-------
2022-08-18 01:59:00.004 INFO [irms-transfer-service,,,] 15 --- [ scheduling-1] c.s.irms.transfer.biz.ProjectManageBiz : -----当前没有导出任务,退出-----
2022-08-18 02:00:00.020 INFO [irms-transfer-service,61d39bab44e29345,61d39bab44e29345,true] 15 --- [ scheduling-1] c.s.i.t.b.b.GeoDataSynTimingTask : 集团库空间坐标附属表定时任务开启
2022-08-18 02:00:00.104 INFO [irms-transfer-service,61d39bab44e29345,61d39bab44e29345,true] 15 --- [ scheduling-1] c.s.i.t.b.b.GeoDataSynTimingTask : 等待集团库数据同步完成
2022-08-18 02:00:15.298 INFO [irms-transfer-service,,,] 15 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
当后台同一时间启动多个定时任务时,会出现冲突
造成上一个定时开启的扫描任务被停掉,无法继续正常执行
原因:基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响;
问题解决
上述博客中提供了三种实现方式
使用@Asyn注解来实现定时任务的异步处理
1.创建线程池(不使用默认提供的线程池,要去自定义)
阿里社区规约:
编写配置类ExecutorConfig:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 自定义线程池
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
/**
* 核心线程
*/
private static final int corePoolSize = 3;
/**
* 最大线程
*/
private static final int maxPoolSize = 6;
/**
* 队列容量
*/
private static final int queueCapacity = 100;
/**
* 保持时间
*/
private static final int keepAliveSeconds = 3;
/**
* 名称前缀
*/
private static final String preFix ="Async-scanTaskExecutor-";
@Bean("scanTaskExecutor")
public Executor scanTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(preFix);
executor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
2. 在异步方法上添加注解
@Async("scanTaskExecutor")
public void test1() {
log.info("导出任务11111");
}
@Async("scanTaskExecutor")
public void test2() {
log.info("导出任务2222");
}
@Async("scanTaskExecutor")
public void test3() {
log.info("导出任务3333");
}
代码中的 @Async(“scanTaskExecutor”) 对应我们自定义线程池中的 @Bean(“scanTaskExecutor”) ,表示使用我们自定义的线程池。
SVC层模拟多个定时任务同时开启
@Scheduled(cron = "0/10 * * * * ?") //每10秒执行一次
public void scanExportTaskAndGenerate111() throws Exception {
log.info("-------定时扫描导出任务111开启-------");
.test1();
}
@Scheduled(cron = "0/10 * * * * ?") //每10秒执行一次
public void scanExportTaskAndGenerate222() throws Exception {
log.info("-------定时扫描导出任务222开启-------");
.test2();
}
@Scheduled(cron = "0/10 * * * * ?") //每10秒执行一次
public void scanExportTaskAndGenerate333() throws Exception {
log.info("-------定时扫描导出任务333开启-------");
.test3();
}
运行效果:
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务111开启-------
--- [Async-Service-3] c.s.irms.transfer.biz.ProjectManageBiz : 导出任务11111
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务开启-------
--- [Async-Service-1] c.s.irms.transfer.biz.ProjectManageBiz : -----当前没有导出任务,退出-----
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务222开启-------
--- [Async-Service-2] c.s.irms.transfer.biz.ProjectManageBiz : 导出任务2222
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务333开启-------
--- [Async-Service-3] c.s.irms.transfer.biz.ProjectManageBiz : 导出任务3333
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务开启-------
--- [Async-Service-1] c.s.irms.transfer.biz.ProjectManageBiz : -----当前没有导出任务,退出-----
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务222开启-------
--- [Async-Service-2] c.s.irms.transfer.biz.ProjectManageBiz : 导出任务2222
--- [ scheduling-1] c.s.i.t.service.impl.ProjectManageSvc : -------定时扫描导出任务333开启-------
注意事项:
- 异步方法使用static修饰
- 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
- 异步方法不能与被调用的异步方法在同一个类中
- 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
- 如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解