最近做一个 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@EnableAsyncpublic 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@Slf4jpublic class DynamicTimedTask {//接受任务的返回结果private ScheduledFuture<?> future;private final ThreadPoolTaskScheduler threadPoolTaskScheduler;public DynamicTimedTask(ThreadPoolTaskScheduler threadPoolTaskScheduler) {this.threadPoolTaskScheduler = threadPoolTaskScheduler;}//实例化一个线程池任务调度类,可以使用自定义的ThreadPoolTaskScheduler@Beanpublic 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 {@Overridepublic void run() {//编写你自己的业务逻辑}}}
