【Spring】Spring的定時任務註解@Scheduled原來如此簡單

1 簡介

定時任務的實現很是多,JDK的Timer、Spring提供的輕量級的Scheduled TaskQuartZLinux Cron等,還有一些分佈式的任務調度框架。本文主要介紹Scheduled Task的使用。java

2 方便的4種方式

註解@Scheduled只能用於知足下面兩個條件的方法上:數據庫

(1)沒有返回類型,或者說返回類型爲void多線程

(2)沒有參數;app

開啓Spring的Scheduler很是簡單,一個註解@EnableScheduling便可:框架

@Configuration
@EnableScheduling
public class SchedulingConfig {
}

若是是Springboot應用,則直接在啓動類上面加上@EnableScheduling就可使用了。異步

2.1 固定延遲fixedDelay

表明下一個任務的開始與上一個任務的結束間隔老是固定的時長,並且老是會等上一個任務完成了,纔會開啓下一個任務。若是需求是有這樣依賴要求的,使用這種模式是很是合適的。代碼以下:async

@Scheduled(fixedDelay = 1000)
public void fixedDelay() {
  log.info("fixedDelay");
}

參數爲1000,表明固定延遲爲1000毫秒,即1秒鐘,因此輸出爲:分佈式

2019-11-19 21:02:43,977 scheduling-1:fixedDelay 
2019-11-19 21:02:44,981 scheduling-1:fixedDelay 
2019-11-19 21:02:45,983 scheduling-1:fixedDelay 
2019-11-19 21:02:46,984 scheduling-1:fixedDelay

2.2 固定頻率fixedRate

2.2.1 正常狀況

定頻任務的特性是任務的執行的時間間隔老是同樣的。好比每1小時執行一次,就是任務執行開始的時間點的時間間隔爲1小時。代碼以下:線程

@Scheduled(fixedRate = 2000)
public void fixedRate() {
  log.info("fixedRate");
}

參數爲2000,則每2秒執行一次,輸出爲:日誌

2019-11-19 21:38:45,073 scheduling-1:fixedRate 
2019-11-19 21:38:47,076 scheduling-1:fixedRate 
2019-11-19 21:38:49,073 scheduling-1:fixedRate 
2019-11-19 21:38:51,075 scheduling-1:fixedRate

2.2.2 默認單線程

須要注意的是它默認是單線程的,不會並行執行。即便是固定頻率,但下一次的任務也必須等到上一次任務執行完畢纔會開始。下面這個例子能很好說明:

@Scheduled(fixedRate = 1000)
public void fixedRateLongTimeTask() throws InterruptedException {
  log.info("fixedRateLongTimeTask");
  Thread.sleep(3000);
}

因爲任務須要執行3秒才能完成,即便fixedRate設置爲1秒,並不能每一秒執行一次,輸出以下:

2019-11-19 21:46:00,108 scheduling-1:fixedRateLongTimeTask 
2019-11-19 21:46:03,113 scheduling-1:fixedRateLongTimeTask 
2019-11-19 21:46:06,113 scheduling-1:fixedRateLongTimeTask 
2019-11-19 21:46:09,117 scheduling-1:fixedRateLongTimeTask

每3次輸出一次。

2.2.3 註解@Async來幫你

上述問題有辦法解決嗎?答案是確定的,並且很是簡單。只須要加一個註解@Async就可使任務能異步多線程地執行了,代碼以下:

@Async
@Scheduled(fixedRate = 1000)
public void fixedRateLongTimeTask() throws InterruptedException {
  log.info("fixedRateLongTimeTask");
  Thread.sleep(3000);
}

經過日誌能夠看出是每秒執行一次的,即便前面的任務尚未完成。並且線程名不同,經過多線程來執行,輸出結果爲:

2019-11-19 21:54:22,261 task-5:fixedRateLongTimeTask 
2019-11-19 21:54:23,257 task-6:fixedRateLongTimeTask 
2019-11-19 21:54:24,257 task-4:fixedRateLongTimeTask 
2019-11-19 21:54:25,257 task-8:fixedRateLongTimeTask 
2019-11-19 21:54:26,259 task-1:fixedRateLongTimeTask 
2019-11-19 21:54:27,262 task-2:fixedRateLongTimeTask 
2019-11-19 21:54:28,260 task-3:fixedRateLongTimeTask

注意:須要指出的是,須要像@EnableScheduling同樣,須要添加配置註解@EnableAsync來打開這個功能開關。另外,若是任務執行時間很長,例如1分鐘,狀況又不同。之後再詳細介紹@Async的使用吧。

2.3 初始延遲initialDelay

初始延遲是用initialDelay來指定的,它能夠延遲第一次任務執行的時間。以下例子的參數爲30秒,則在啓動30秒後,纔開始執行第一次。能夠減輕項目啓動的負擔,也能夠爲任務執行前準備數據。

@Scheduled(fixedDelay = 1000, initialDelay = 30*1000)
public void fixedDelayWithIntialDelay() {
  log.info("fixedDelayWithIntialDelay");
}

輸出以下:

2019-11-19 22:10:02,092 main:Tomcat started on port(s): 443 (http) with context path '' 
2019-11-19 22:10:02,095 main:Started DemoApplication in 1.272 seconds (JVM running for 1.767) 
2019-11-19 22:10:32,063 scheduling-1:fixedDelayWithIntialDelay 
2019-11-19 22:10:33,067 scheduling-1:fixedDelayWithIntialDelay 
2019-11-19 22:10:34,069 scheduling-1:fixedDelayWithIntialDelay 
2019-11-19 22:10:35,069 scheduling-1:fixedDelayWithIntialDelay

能夠看出,在項目啓動後30秒左右,纔開始執行任務。

2.4 Cron表達式

上述提供的功能並不能知足定時任務調度的全部需求,好比須要每月1號發送短信,每週六作數據分析等。這裏Cron表達式就派上用場了。

下面的例子表示每當秒數爲06的時候就執行。代碼以下:

@Scheduled(cron = "6 * * ? * *")
public void cron() {
  log.info("cron");
}

結果以下:

2019-11-19 22:20:06,003 scheduling-1:cron 
2019-11-19 22:21:06,004 scheduling-1:cron 
2019-11-19 22:22:06,002 scheduling-1:cron

Cron表達式功能很是強大,網上資料很豐富,這裏不展開講了。

3 參數配置化

以前的例子都將參數寫死在代碼上了,若是須要更靈活,其實能夠用參數來配置。這樣須要修改參數的時候,不用修改代碼、編譯打包再部署了,直接修改配置文件便可。

代碼以下:

@Scheduled(cron = "${pkslow.cron}")
public void cronWithConfig() {
  log.info("cronWithConfig");
}

在application.properties配置以下:

pkslow.cron=* * * ? * *

代碼1秒執行一次。

4 若是她忽然消失了

因爲Spring的Scheduler默認是單線程的,這樣會存在一個問題,若是某個任務執行卡住了,那就沒法繼續往下執行了。在日誌上表現就是忽然消失了。這種狀況出現的機率仍是不小的,如操做數據庫死鎖了,http請求timeout爲無限等待,還有其它緣由的死鎖等。

當遇到這種狀況,應經過命令jstack pid > pid.ThreadDump.txt獲取當前線程狀況,而後分析是否真的是卡住了,卡在了哪一個環節,而後再分析具體代碼。經過設置超時或重試等方法來解決。

5 結論

本文主要介紹了Spring的定時任務註解@Scheduled的使用,講述了多種方式的使用和配置。它很是方便簡潔,對於簡單的定時任務足以應對了。


歡迎關注公衆號<南瓜慢說>,將持續爲你更新...

file

多讀書,多分享;多寫做,多整理。

相關文章
相關標籤/搜索