定時任務調度功能在咱們的開發中是很是常見的,隨便舉幾個例子:定時清除一些過時的數據,定時發送郵件等等,實現定時任務調度的方式也十分多樣,本篇文章主要學習各類實現定時任務調度方式的優缺點,以便爲往後選擇的時候提供必定的參考。java
介紹Timer實現定時任務。git
介紹ScheduledExecutorService實現定時任務。spring
介紹SpringBoot使用SpringTask實現定時任務。springboot
介紹SpringBoot使用SpringTask實現異步任務。多線程
基於JDK自帶的java.util.Timer
,經過調度java.util.TimeTask
讓某一段程序按某一固定間隔,在某一延時以後定時執行。異步
缺點:async
public class DemoTimer { //延時時間 private static final long DELAY = 3000; //間隔時間 private static final long PERIOD = 5000; public static void main(String[] args) { // 定義要執行的任務 TimerTask task = new TimerTask() { @Override public void run() { System.out.println("任務執行 --> " + LocalDateTime.now()); } }; Timer timer = new Timer(); timer.schedule(task, DELAY, PERIOD); } }
阿里巴巴開發規範明確規定:但願開發者使用ScheduledExecutorService代替Timer。ide
多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。spring-boot
public class DemoScheduledExecutorService { //延時時間 private static final long DELAY = 3000; //間隔時間 private static final long PERIOD = 5000; public static void main(String[] args) { Runnable task = new Runnable() { @Override public void run() { System.out.println("任務執行 --> " + LocalDateTime.now()); } }; ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleAtFixedRate(task, DELAY, PERIOD, TimeUnit.MILLISECONDS); } }
Spring爲咱們提供了異步執行任務調度的方式,提供TaskExecutor,TaskScheduler接口,而SpringBoot的自動配置類org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
爲咱們默認注入了他們的實現:ThreadPoolTaskScheduler
,本質上是ScheduledExecutorService
的封裝,加強在調度時間上的功能。學習
@ConditionalOnClass(ThreadPoolTaskScheduler.class) @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(TaskSchedulingProperties.class) @AutoConfigureAfter(TaskExecutionAutoConfiguration.class) public class TaskSchedulingAutoConfiguration { @Bean @ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class }) public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) { return builder.build(); } @Bean @ConditionalOnMissingBean public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties, ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) { TaskSchedulerBuilder builder = new TaskSchedulerBuilder(); builder = builder.poolSize(properties.getPool().getSize()); Shutdown shutdown = properties.getShutdown(); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); builder = builder.customizers(taskSchedulerCustomizers); return builder; } }
Spring Task是Spring Framework中的模塊,咱們只需引入spring-boot-starter
依賴就能夠了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
@Configuration @EnableScheduling public class ScheduleConfig { }
@Configuration
代表這是個配置類。@EnableScheduling
代表啓用Spring的定時任務調度功能。@Component @Slf4j public class DemoTask { private final AtomicInteger counts = new AtomicInteger(); @Scheduled(cron = "0/5 * * * * *") public void execute() { log.info("[定時任務第 {} 次執行]", counts.incrementAndGet()); } }
@Component
代表該類須要被掃描,以便於Spring容器管理。@Scheduled
標註須要調度執行的方法,定義執行規則,其必須指定cron
、fixedDelay
或fixedRate
三個屬性其中一個。
cron
:定義Spring cron表達式,網上有在線cron生成器,能夠對照着編寫符合需求的定時任務。fixedDelay
:固定執行間隔,單位:毫秒。注意,以調用完成時刻爲開始計時時間。fixedRate
:固定執行間隔,單位:毫秒。注意,以調用開始時刻爲開始計時時間。@SpringBootApplication public class SpringBootTaskApplication { public static void main(String[] args) { SpringApplication.run(SpringBootTaskApplication.class, args); } }
Spring Task 調度任務的配置,對應 TaskSchedulingProperties 配置類。SpringBoot容許咱們在yml或properties定製這些外部化配置,若是不配置也是沒有關係的,自動配置已經給你一套默認的值了。
spring: task: scheduling: thread-name-prefix: summerday- # 線程池的線程名的前綴。默認爲 scheduling- ,建議根據本身應用來設置 pool: size: 10 # 線程池大小。默認爲 1 ,根據本身應用來設置 shutdown: await-termination: true # 應用關閉時,是否等待定時任務執行完成。默認爲 false ,建議設置爲 true await-termination-period: 60 # 等待任務完成的最大時長,單位爲秒。默認爲 0 ,根據本身應用來設置
# 初始化一個 ThreadPoolTaskScheduler 任務調度器 2020-11-30 23:04:51.886 INFO 10936 --- [ restartedMain] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler' # 每5s執行一次定時任務 2020-11-30 23:04:55.002 INFO 10936 --- [ summerday-1] com.hyh.task.DemoTask : [定時任務第 1 次執行] 2020-11-30 23:05:00.002 INFO 10936 --- [ summerday-1] com.hyh.task.DemoTask : [定時任務第 2 次執行] 2020-11-30 23:05:05.002 INFO 10936 --- [ summerday-2] com.hyh.task.DemoTask : [定時任務第 3 次執行] 2020-11-30 23:05:10.001 INFO 10936 --- [ summerday-1] com.hyh.task.DemoTask : [定時任務第 4 次執行] 2020-11-30 23:05:15.002 INFO 10936 --- [ summerday-3] com.hyh.task.DemoTask : [定時任務第 5 次執行]
SpringTask除了@Scheduled、@EnableScheduling
同步定時任務以外,還有@Async、@EnableAsync
開啓異步的定時任務調度。
SpringBoot自動配置類對異步的支持:org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
@Async @Scheduled(cron = "0/1 * * * * *") public void asyncTask() { sleep(); System.out.println(Thread.currentThread().getName() + " async-task 執行,當前時間: " + LocalDateTime.now()); }
@Configuration @EnableScheduling // 同步 @EnableAsync // 異步 public class ScheduleConfig { }
spring: task: # Spring 執行器配置,對應 TaskExecutionProperties 配置類。對於 Spring 異步任務,會使用該執行器。 execution: thread-name-prefix: async- # 線程池的線程名的前綴。默認爲 task- ,建議根據本身應用來設置 pool: # 線程池相關 core-size: 8 # 核心線程數,線程池建立時候初始化的線程數。默認爲 8 。 max-size: 20 # 最大線程數,線程池最大的線程數,只有在緩衝隊列滿了以後,纔會申請超過核心線程數的線程。默認爲 Integer.MAX_VALUE keep-alive: 60s # 容許線程的空閒時間,當超過了核心線程以外的線程,在空閒時間到達以後會被銷燬。默認爲 60 秒 queue-capacity: 200 # 緩衝隊列大小,用來緩衝執行任務的隊列的大小。默認爲 Integer.MAX_VALUE 。 allow-core-thread-timeout: true # 是否容許核心線程超時,即開啓線程池的動態增加和縮小。默認爲 true 。 shutdown: await-termination: true # 應用關閉時,是否等待定時任務執行完成。默認爲 false ,建議設置爲 true await-termination-period: 60 # 等待任務完成的最大時長,單位爲秒。默認爲 0 ,根據本身應用來設置
@Component public class DemoAsyncTask { @Scheduled(cron = "0/1 * * * * *") public void synTask() { sleep(); System.out.println(Thread.currentThread().getName() + " syn-task 執行,當前時間: " + LocalDateTime.now()); } @Async @Scheduled(cron = "0/1 * * * * *") public void asyncTask() { sleep(); System.out.println(Thread.currentThread().getName() + " async-task 執行,當前時間: " + LocalDateTime.now()); } private void sleep() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
同時開啓同步和異步任務,假設任務自己耗時較長,且間隔較短:間隔1s,執行10s,同步與異步執行的差別就此體現。
能夠看到,同步任務並無每間隔1s就執行,而是串行在一塊兒,等前一個任務執行完才執行。而異步任務則不同,成功將串行化的任務並行化。
本文內容均爲對優秀博客及官方文檔總結而得,原文地址均已在文中參考閱讀處標註。最後,文中的代碼樣例已經所有上傳至Gitee:https://gitee.com/tqbx/springboot-samples-learn,另有其餘SpringBoot的整合哦。