在程序開發的過程當中,常常會使用定時任務來實現一些功能,好比:java
在Spring Boot中,咱們可使用@Scheduled註解來快速的實現這些定時任務。git
@Scheduled註解主要支持如下3種方式:github
那麼接下來,咱們講解下具體的實現方式以及這3種方式之間的區別。spring
首先,須要在啓動類上添加@EnableScheduling註解:springboot
package com.zwwhnly.springbootdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SpringbootdemoApplication {
/*其餘代碼*/
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
複製代碼
而後,新建一個定時任務測試類TestSchedule,該類須要添加註解@Componentpost
package com.zwwhnly.springbootdemo;
import org.springframework.stereotype.Component;
@Component
public class TestSchedule {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
複製代碼
添加一個測試方法testFixedDelay,該方法添加註解@Scheduled,這裏咱們先使用最好理解的fixedDelay,爲了能看到效果,咱們每隔5秒輸出下系統的當前時間,以下所示:測試
// 上次任務執行結束後,間隔5秒再執行下次任務
@Scheduled(fixedDelay = 5000)
public void testFixedDelay() {
System.out.println("當前時間:" + simpleDateFormat.format(new Date()));
}
複製代碼
啓動項目,咱們會看到運行結果以下:atom
當前時間:2019-04-09 10:28:56 當前時間:2019-04-09 10:29:01 當前時間:2019-04-09 10:29:06 當前時間:2019-04-09 10:29:11 當前時間:2019-04-09 10:29:16spa
從運行結果來看,確實是每隔5秒輸出一次。.net
可是在實際項目中,不可能這麼規律,好比方法的執行時間超過了5秒呢(這個應該很常見),那麼彼時程序又是如何執行的呢?
咱們修改下程序(代碼參考文章[肥朝]原理暫且不談,定時器你當真會用?):
private List<Integer> index = Arrays.asList(8 * 1000, 3 * 1000, 6 * 1000, 2 * 1000, 2 * 1000);
private AtomicInteger atomicInteger = new AtomicInteger(0);
// 上次任務執行結束後,間隔5秒再執行下次任務
@Scheduled(fixedDelay = 5000)
public void testFixedDelay() throws Exception {
int i = atomicInteger.get();
if (i < 5) {
Integer sleepTime = index.get(i);
System.out.println("當前時間:" + simpleDateFormat.format(new Date()));
Thread.sleep(sleepTime);
atomicInteger.getAndIncrement();
}
}
複製代碼
此時的運行結果爲:
當前時間:2019-04-18 14:09:26
當前時間:2019-04-18 14:09:39
當前時間:2019-04-18 14:09:47
當前時間:2019-04-18 14:09:58
當前時間:2019-04-18 14:10:05
讓咱們來分析下運行結果:
第2次輸出的時間距離第1次輸出的時間間隔爲13秒(即方法的執行時間8s+定義的延遲時間5s)。
第3次輸出的時間距離第2次輸出的時間間隔爲8秒(即方法的執行時間3s+定義的延遲時間5s)。
第4次輸出的時間距離第3次輸出的時間間隔爲11秒(即方法的執行時間6s+定義的延遲時間5s)。
第5次輸出的時間距離第4次輸出的時間間隔爲7秒(即方法的執行時間2s+定義的延遲時間5s)。
由此咱們能夠得出結論:使用fixedDelay,無論方法的執行時間是否超過定義的時間5s,上一個任務執行完成後,都會延遲5s再執行下一個任務。
添加測試方法testFixedRate,此次咱們使用fixedRate。
// 每5秒執行一次
@Scheduled(fixedRate = 5000)
public void testFixedRate() {
System.out.println("當前時間:" + simpleDateFormat.format(new Date()));
}
複製代碼
啓動項目,咱們會看到運行結果以下:
從運行結果來看,也是每隔5秒輸出一次。
可是在實際項目中,不可能這麼規律,好比方法的執行時間超過了5秒呢(這個應該很常見),那麼彼時程序又是如何執行的呢?
咱們來修改下程序,讓方法每次的執行時間不固定:
private List<Integer> index = Arrays.asList(8 * 1000, 3 * 1000, 6 * 1000, 2 * 1000, 2 * 1000);
private AtomicInteger atomicInteger = new AtomicInteger(0);
@Scheduled(fixedRate = 5000)
public void testFixedRate() throws Exception {
int i = atomicInteger.get();
if (i < 5) {
Integer sleepTime = index.get(i);
System.out.println("當前時間:" + simpleDateFormat.format(new Date()));
Thread.sleep(sleepTime);
atomicInteger.getAndIncrement();
}
}
複製代碼
此時的運行結果爲:
當前時間:2019-04-18 15:05:46 當前時間:2019-04-18 15:05:54 當前時間:2019-04-18 15:05:57 當前時間:2019-04-18 15:06:03 當前時間:2019-04-18 15:06:06
以前個人理解是上一個任務執行完成後,會當即執行下一個任務,可是細心的網友會發現,最後一次輸出的時間明明離上次的時間間隔了3秒,按照當即執行的說法,應該間隔2秒纔對。
直到看到這篇文章[肥朝]原理暫且不談,定時器你當真會用?,我才發現本身以前的理解是錯誤的,在此也很是感謝這篇文章的做者肥朝。
文章中的例子很是形象的解釋了以上結果的緣由,這裏借鑑下(若有侵權,可聯繫我刪除)。
拿洗澡的這個例子來講。
你能夠理解爲舍長預算每一個同窗洗澡的時間是5秒。
可是第一個同窗進去洗澡,用了8秒。
第二個同窗原本應該在第5秒的時候就進去的,結果第一個同窗出來的時候,已是第8秒了,那麼舍長就趕忙催第二個同窗進去,把時間進度追回來。
第二個同窗知道時間緊,洗了3秒就出來.此時舍長髮現,第三個同窗,本來應該是在第10秒進去的,結果如今已經到了第11秒(8+3),那麼就趕忙催第三個同窗進去。
第三個同窗沉醉其中,難以自拔的洗了6秒.出來的時候已是第17秒(8+3+6).舍長掐指一算,發現第四個同窗本來應該是第15秒的時候就進去了.結果如今都17秒了,時間不等人,催促第四個同窗進去趕忙洗。
第四個同窗只洗了2秒就出來了,出來的時候,舍長看了一下時間,是第19秒.有原則的舍長髮現,第5個同窗本來預算是20秒的時候進去的,結果如今才19秒,不行,那讓他在外面玩1秒的手機,等20秒的時候再進去。
相比於上面講的兩種方式,cron表達式顯得更加靈活,由於它基本知足各類場景的配置需求,好比固定頻率執行,固定某個時間點執行等。
首先,咱們使用cron表達式實現上述例子中的每隔5秒執行一次:
@Scheduled(cron = "0/5 * * * * ?")
public void testCron() {
System.out.println("當前時間:" + simpleDateFormat.format(new Date()));
}
複製代碼
運行結果爲:
當前時間:2019-04-09 11:00:50 當前時間:2019-04-09 11:00:55 當前時間:2019-04-09 11:01:00 當前時間:2019-04-09 11:01:05 當前時間:2019-04-09 11:01:10
修改下代碼,讓每次方法執行時延遲不一樣的時間:
private List<Integer> index = Arrays.asList(8 * 1000, 3 * 1000, 6 * 1000, 2 * 1000, 2 * 1000);
private AtomicInteger atomicInteger = new AtomicInteger(0);
@Scheduled(cron = "0/5 * * * * ?")
public void testCron() throws Exception {
int i = atomicInteger.get();
if (i < 5) {
Integer sleepTime = index.get(i);
System.out.println("當前時間:" + simpleDateFormat.format(new Date()));
Thread.sleep(sleepTime);
atomicInteger.getAndIncrement();
}
}
複製代碼
此時的運行結果爲:
當前時間:2019-04-18 16:38:10 當前時間:2019-04-18 16:38:20 當前時間:2019-04-18 16:38:25 當前時間:2019-04-18 16:38:35 當前時間:2019-04-18 16:38:40
也許你會發現,有些時間間隔爲10s,有些時間間隔爲5s,這是爲何呢?
這裏再次借鑑下肥朝大佬文章中的解釋(若有侵權,可聯繫我刪除):
仍然拿洗澡的這個例子來講。
你能夠理解爲5s就是一個週期.這就至關於在宿舍洗澡,由於只有一個洗澡位置(單線程),因此每次只能進去一我的,而後舍長在門口,每5s看一下有沒有空位,有空位的話叫下一個進去洗澡。
第5秒的時候,舍長看了一下,發現第一個同窗尚未出來(由於他洗了8s)。
第二個週期,也就是第10秒的時候再看一下.發現已經有空位了,那麼就叫第二個同窗進去洗。
第三個週期,也就是15秒的時候,又瞄了一眼,發現有空位了(由於前兩位同窗用了8+3秒),叫第三個同窗進去洗。
第四個週期,也就是20秒的時候,發現沒有空位。
第五個週期的時候,也就是25秒的時候.發現有空位了,接着叫下一個進去洗.剩下的再也不分析。
若是想要設置成天天的某個時間點執行,好比個人我的博客www.zwwhnly.com/就是每晚20:00定時拉取GitHub代碼並使用Jekyll編譯到Nginx的目錄下實現的自動發佈。
實現這個配置的cron表達式爲:0 0 20 * * ? 。
更多的cron表達式,能夠到cron.qqe2.com/去自定義,勾選好會自動生成cron表達式,很是方便。
github.com/zwwhnly/spr…,歡迎你們下載,有問題能夠多多交流。