最近做一个 SpringBoot 小项目时发现,项目正常启动后,一个定时任务正常执行,但是另一个定时任务却没开启,刚开始以为是哪里配置出了问题,经过一些排查发现两个定时任务线程名字是一样的,说明是同一个线程执行的,其中一个阻塞了,另一个自然不会执行。
SpringBoot 默认的定时任务
通过注解开启定时任务@EnableScheduling
指定定时任务执行的周期或者 cron 表达式,作用在方法上@Scheduled(cron = **"0 0 9,18 * * ? "**)
默认是单线程的~
多线程执行
通过注解 @EnableAsync
启用异步执行
增加线程池的配置
package com.gaoshan.ebpc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* SpringBoot 定时任务 异步执行与线程池配置
*/
@EnableScheduling
@Configuration
@EnableAsync
public class ScheduleConfig {
/**
* 默认线程池配置
*
* @return {@link Executor}
*/
@Bean(name = "defaultExecutor")
public Executor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("ebpc-default-schedule-");
executor.setMaxPoolSize(8);
executor.setCorePoolSize(4);
executor.setQueueCapacity(10);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/**
* 消费者线程池配置
*
* @return {@link Executor}
*/
@Bean(name = "executor4Consumer")
public Executor executor4Consumer() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("ebpc-consumer-schedule-");
executor.setMaxPoolSize(8);
executor.setCorePoolSize(4);
executor.setQueueCapacity(20);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
定时任务异步执行 @Async("defaultExecutor")
/**
* 2min 执行一次,查询最新的区块,并加入队列
*/
@Async("defaultExecutor")
@Scheduled(fixedRate = 120_000, initialDelay = 10_000)
public void producer() {
if (!ebpcEnable) {
return;
}
if (pullBlockProcessor.getEthBlockQueue().isEmpty()) {
log.info("+++++ 开始执行 NewBlockSyncTask +++++");
pullBlockProcessor.sync(100);
log.info("+++++ NewBlockSyncTask 执行结束 +++++");
}
}
@Async
注解的说明 Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type’s methods are considered as asynchronous. Note, however, that @Async is not supported on methods declared within a @Configuration class.将方法标记为异步执行候选方法的注解。 也可以在类型级别使用,在这种情况下,类型的所有方法都被视为异步的。 但是,请注意,@Configuration类中声明的方法不支持@Async。
动态定时任务
从数据库读取 cron 表达式,利用反射机制执行方法,类似 quartz ~
package com.gaoshan.ebpc.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.concurrent.ScheduledFuture;
@Component
@Slf4j
public class DynamicTimedTask {
//接受任务的返回结果
private ScheduledFuture<?> future;
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
public DynamicTimedTask(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
}
//实例化一个线程池任务调度类,可以使用自定义的ThreadPoolTaskScheduler
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
/**
* 启动定时任务
*/
public boolean startCron() {
//从数据库动态获取执行周期
String cron = "0/2 * * * * ? ";
future = threadPoolTaskScheduler.schedule(new CheckModelFile(), Instant.parse(cron));
return true;
}
/**
* 停止定时任务
*
*/
public boolean stopCron() {
boolean flag = false;
if (future != null) {
boolean cancel = future.cancel(true);
if (cancel) {
flag = true;
}
} else {
flag = true;
}
return flag;
}
static class CheckModelFile implements Runnable {
@Override
public void run() {
//编写你自己的业务逻辑
}
}
}