SpringBoot 是為了簡化 Spring 應用的創建、運行、調試、部署等一系列問題而誕生的產物,自動裝配的特性讓我們可以更好的關注業務本身而不是外部的XML配置,我們只需遵循規范,引入相關的依賴就可以輕易的搭建出一個 WEB 工程
在我們日常開發中,經常會遇到 數據定時增量同步、定時發送郵件、爬蟲定時抓取 的需求;這時我們可以采用定時任務的方式去進行工作…..
定時任務概述
定時任務:顧名思義就是在指定/特定的時間進行工作,比如我們的手機鬧鍾,它就是一種定時任務。
實現方式
Timer: JDK自帶的java.util.Timer;通過調度java.util.TimerTask的方式 讓程序按照某一個頻度執行,但不能在指定時間運行。 一般用的較少。
ScheduledExecutorService: JDK1.5新增的,位於java.util.concurrent包中;是基於線程池設計的定時任務類,每個調度任務都會被分配到線程池中,並發執行,互不影響。
Spring Task: Spring3.0 以后新增了task,一個輕量級的Quartz,功能夠用,用法簡單。
Quartz: 功能最為強大的調度器,可以讓程序在指定時間執行,也可以按照某一個頻度執行,它還可以動態開關,但是配置起來比較復雜。現如今開源社區中已經很多基於Quartz 實現的分布式定時任務項目(xxl-job、elastic-job)。
Timer 方式
基於 Timer 實現的定時調度,基本就是手擼代碼,目前應用較少,不是很推薦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
package com.battcn.timer;
import java.time.LocalDateTime; import java.util.Timer; import java.util.TimerTask;
|
基於 ScheduledExecutorService
與Timer很類似,但它的效果更好,多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中有一個因任務報錯沒有捕獲拋出的異常,其它任務便會自動終止運行,使用 ScheduledExecutorService 則可以規避這個問題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
package com.battcn.scheduled;
import java.time.LocalDateTime; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit;
|
Spring Task(本章關鍵)
導入依賴
在 pom.xml 中添加 spring-boot-starter-web 依賴即可,它包含了spring-context,定時任務相關的就屬於這個JAR下的org.springframework.scheduling包中
1 2 3 4 5 6
|
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
|
定時任務
@Scheduled 定時任務的核心
cron: cron表達式,根據表達式循環執行,與fixedRate屬性不同的是它是將時間進行了切割。(@Scheduled(cron = "0/5 * * * * *")任務將在5、10、15、20...這種情況下進行工作)
fixedRate: 每隔多久執行一次;(@Scheduled(fixedRate = 1000) 假設第一次工作時間為2018-05-29 16:58:28,工作時長為3秒,那么下次任務的時候就是2018-05-29 16:58:31,配置成異步后,只要到了執行時間就會開辟新的線程工作),如果(@Scheduled(fixedRate = 3000) 假設第一次工作時間為2018-05-29 16:58:28,工作時長為1秒,那么下次任務的時間依然是2018-05-29 16:58:31)
fixedDelay: 當前任務執行完畢后等待多久繼續下次任務(@Scheduled(fixedDelay = 3000) 假設第一次任務工作時間為2018-05-29 16:54:33,工作時長為5秒,那么下次任務的時間就是2018-05-29 16:54:41)
initialDelay: 第一次執行延遲時間,只是做延遲的設定,與fixedDelay關系密切,配合使用,相輔相成。
@Async 代表該任務可以進行異步工作,由原本的串行改為並行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
package com.battcn.task;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
|
cron表達式在線生成: http://www.pdtools.net/tools/becron.jsp
主函數
@EnableScheduling 注解表示開啟對@Scheduled注解的解析;同時new ThreadPoolTaskScheduler()也是相當的關鍵,通過閱讀過源碼可以發現默認情況下的 private volatile int poolSize = 1;這就導致了多個任務的情況下容易出現競爭情況(多個任務的情況下,如果第一個任務沒執行完畢,后續的任務將會進入等待狀態)。
@EnableAsync 注解表示開啟@Async注解的解析;作用就是將串行化的任務給並行化了。(@Scheduled(cron = "0/1 * * * * *")假設第一次工作時間為2018-05-29 17:30:55,工作周期為3秒;如果不加@Async那么下一次工作時間就是2018-05-29 17:30:59;如果加了@Async 下一次工作時間就是2018-05-29 17:30:56)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
package com.battcn;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
測試
完成准備事項后,啟動Chapter15Application,觀察日志信息如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
2018-05-29 17:35:51.479 INFO 32640 --- [taskScheduler-1] com.battcn.task.SpringTaskDemo : scheduled2 每1秒執行一次:2018-05-29T17:35:51.479 2018-05-29 17:35:52.005 INFO 32640 --- [taskScheduler-3] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:52.005 2018-05-29 17:35:53.002 INFO 32640 --- [taskScheduler-5] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:53.002 2018-05-29 17:35:53.468 INFO 32640 --- [taskScheduler-2] com.battcn.task.SpringTaskDemo : scheduled3 上次執行完畢后隔3秒繼續執行:2018-05-29T17:35:53.468 2018-05-29 17:35:54.002 INFO 32640 --- [taskScheduler-6] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:54.002 2018-05-29 17:35:54.479 INFO 32640 --- [taskScheduler-7] com.battcn.task.SpringTaskDemo : scheduled2 每1秒執行一次:2018-05-29T17:35:54.479 2018-05-29 17:35:55.002 INFO 32640 --- [taskScheduler-8] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:55.002 2018-05-29 17:35:56.001 INFO 32640 --- [taskScheduler-1] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:56.001 2018-05-29 17:35:57.001 INFO 32640 --- [taskScheduler-3] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:57.001 2018-05-29 17:35:57.479 INFO 32640 --- [taskScheduler-7] com.battcn.task.SpringTaskDemo : scheduled2 每1秒執行一次:2018-05-29T17:35:57.479 2018-05-29 17:35:58.003 INFO 32640 --- [taskScheduler-4] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:58.003 2018-05-29 17:35:59.001 INFO 32640 --- [taskScheduler-5] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:35:59.001 2018-05-29 17:36:00.002 INFO 32640 --- [taskScheduler-6] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:36:00.002 2018-05-29 17:36:00.480 INFO 32640 --- [taskScheduler-7] com.battcn.task.SpringTaskDemo : scheduled2 每1秒執行一次:2018-05-29T17:36:00.480 2018-05-29 17:36:01.001 INFO 32640 --- [taskScheduler-8] com.battcn.task.SpringTaskDemo : scheduled1 每1秒執行一次:2018-05-29T17:36:01.001 2018-05-29 17:36:01.470 INFO 32640 --- [taskScheduler-9] com.battcn.task.SpringTaskDemo : scheduled3 上次執行完畢后隔3秒繼續執行:2018-05-29T17:36:01.470
|