最近做一个 SpringBoot 小项目时发现,项目正常启动后,一个定时任务正常执行,但是另一个定时任务却没开启,刚开始以为是哪里配置出了问题,经过一些排查发现两个定时任务线程名字是一样的,说明是同一个线程执行的,其中一个阻塞了,另一个自然不会执行。

SpringBoot 默认的定时任务

通过注解开启定时任务
@EnableScheduling

指定定时任务执行的周期或者 cron 表达式,作用在方法上
@Scheduled(cron = **"0 0 9,18 * * ? "**)

默认是单线程的~

多线程执行

通过注解 @EnableAsync 启用异步执行

增加线程池的配置

  1. package com.gaoshan.ebpc.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.scheduling.annotation.EnableAsync;
  5. import org.springframework.scheduling.annotation.EnableScheduling;
  6. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  7. import java.util.concurrent.Executor;
  8. import java.util.concurrent.ThreadPoolExecutor;
  9. /**
  10. * SpringBoot 定时任务 异步执行与线程池配置
  11. */
  12. @EnableScheduling
  13. @Configuration
  14. @EnableAsync
  15. public class ScheduleConfig {
  16. /**
  17. * 默认线程池配置
  18. *
  19. * @return {@link Executor}
  20. */
  21. @Bean(name = "defaultExecutor")
  22. public Executor executor() {
  23. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  24. executor.setThreadNamePrefix("ebpc-default-schedule-");
  25. executor.setMaxPoolSize(8);
  26. executor.setCorePoolSize(4);
  27. executor.setQueueCapacity(10);
  28. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  29. return executor;
  30. }
  31. /**
  32. * 消费者线程池配置
  33. *
  34. * @return {@link Executor}
  35. */
  36. @Bean(name = "executor4Consumer")
  37. public Executor executor4Consumer() {
  38. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  39. executor.setThreadNamePrefix("ebpc-consumer-schedule-");
  40. executor.setMaxPoolSize(8);
  41. executor.setCorePoolSize(4);
  42. executor.setQueueCapacity(20);
  43. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  44. return executor;
  45. }
  46. }

定时任务异步执行
@Async("defaultExecutor")

  1. /**
  2. * 2min 执行一次,查询最新的区块,并加入队列
  3. */
  4. @Async("defaultExecutor")
  5. @Scheduled(fixedRate = 120_000, initialDelay = 10_000)
  6. public void producer() {
  7. if (!ebpcEnable) {
  8. return;
  9. }
  10. if (pullBlockProcessor.getEthBlockQueue().isEmpty()) {
  11. log.info("+++++ 开始执行 NewBlockSyncTask +++++");
  12. pullBlockProcessor.sync(100);
  13. log.info("+++++ NewBlockSyncTask 执行结束 +++++");
  14. }
  15. }

@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 ~

  1. package com.gaoshan.ebpc.config;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
  5. import org.springframework.stereotype.Component;
  6. import java.time.Instant;
  7. import java.util.concurrent.ScheduledFuture;
  8. @Component
  9. @Slf4j
  10. public class DynamicTimedTask {
  11. //接受任务的返回结果
  12. private ScheduledFuture<?> future;
  13. private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
  14. public DynamicTimedTask(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
  15. this.threadPoolTaskScheduler = threadPoolTaskScheduler;
  16. }
  17. //实例化一个线程池任务调度类,可以使用自定义的ThreadPoolTaskScheduler
  18. @Bean
  19. public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
  20. return new ThreadPoolTaskScheduler();
  21. }
  22. /**
  23. * 启动定时任务
  24. */
  25. public boolean startCron() {
  26. //从数据库动态获取执行周期
  27. String cron = "0/2 * * * * ? ";
  28. future = threadPoolTaskScheduler.schedule(new CheckModelFile(), Instant.parse(cron));
  29. return true;
  30. }
  31. /**
  32. * 停止定时任务
  33. *
  34. */
  35. public boolean stopCron() {
  36. boolean flag = false;
  37. if (future != null) {
  38. boolean cancel = future.cancel(true);
  39. if (cancel) {
  40. flag = true;
  41. }
  42. } else {
  43. flag = true;
  44. }
  45. return flag;
  46. }
  47. static class CheckModelFile implements Runnable {
  48. @Override
  49. public void run() {
  50. //编写你自己的业务逻辑
  51. }
  52. }
  53. }