定時任務在實際開發中有著廣泛的用途,本文主要幫助你構建定時任務的知識體系,同時展示Timer 的schedule和scheduleAtFixedRate例子;后續的文章中我們將逐一介紹其它常見的與SpringBoot的集成。
知識準備
需要對定時任務的使用場景和常見的實現方式。
什么樣的場景會使用定時任務?
比如每天/每周/每月生成日志匯總,定時發送推送信息,定時生成數據表格等
定時任務有哪些實現方式?
首先你需要構建如下實現定時任務的知識體系。在后續的文章中我們將逐一介紹在SpringBoot下的集成。
- 定時任務基礎
-
- Cron表達式
- Linux定時任務工具crontb
- JDK內置
-
- Timer
- ScheduleExecutorService
- Netty
-
- HashedWheelTimer
- Spring
-
- Spring自帶Schedule
- Spring集成Quartz
- 分布式集群
-
- Quartz持久化JDBC方式
- Elastic-job
- xxl-job
Timer實現案例
Timer 的schedule和scheduleAtFixedRate例子如下。
schedule延遲任務
執行定時任務,延遲1秒開始執行。
@SneakyThrows
public static void timer() {// start timerTimer timer = new Timer();timer.schedule(new TimerTask() {public void run() {log.info("timer-task @{}", LocalDateTime.now());}}, 1000);// waiting to process(sleep to mock)Thread.sleep(3000);// stop timertimer.cancel();
}
輸出
10:05:47.440 [Timer-0] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-task @2021-10-01T20:05:47.436
schedule周期任務
延遲0.5秒開始執行,每秒執行一次, 10秒后停止。
@SneakyThrows
public static void timerPeriod() {// start timerTimer timer = new Timer();timer.schedule(new TimerTask() {@SneakyThrowspublic void run() {log.info("timer-period-task @{}", LocalDateTime.now());Thread.sleep(100); // 可以設置的執行時間, 來測試當執行時間大于執行周期時任務執行的變化 }}, 500, 1000);// waiting to process(sleep to mock)Thread.sleep(10000);// stop timertimer.cancel();
}
輸出
10:05:49.781 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:49.781
10:05:50.781 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:50.781
10:05:51.781 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:51.781
10:05:52.781 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:52.781
10:05:53.782 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:53.782
10:05:54.783 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:54.783
10:05:55.783 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:55.783
10:05:56.784 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:56.784
10:05:57.785 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:57.785
10:05:58.786 [Timer-1] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-period-task @2021-10-01T10:05:58.786
scheduleAtFixedRate
延遲0.5秒開始執行,每秒執行一次, 10秒后停止。
同時測試某次任務執行時間大于周期時間的變化。
@SneakyThrows
public static void timerFixedRate() {// start timerTimer timer = new Timer();timer.scheduleAtFixedRate(new TimerTask() {int count = 0;@SneakyThrowspublic void run() {if (count++==2) {Thread.sleep(5000); // 某一次執行時間超過了period(執行周期)}log.info("timer-fixedRate-task @{}", LocalDateTime.now());}}, 500, 1000);// waiting to process(sleep to mock)Thread.sleep(10000);// stop timertimer.cancel();
}
輸出
10:05:59.781 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:05:59.781
10:06:00.782 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:00.782
10:06:06.783 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:06.783
10:06:06.783 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:06.783
10:06:06.783 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:06.783
10:06:06.783 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:06.783
10:06:06.783 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:06.783
10:06:06.783 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:06.783
10:06:07.781 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:07.781
10:06:08.781 [Timer-2] INFO tech.aizer.springboot.schedule.timer.timertest.TimerTester - timer-fixedRate-task @2021-10-01T10:06:08.781
(你會發現周期執行1秒中執行一次,但是某次執行了5秒,這時候,后續的任務會加快執行進度,一次性就執行了,執行的時間都是10:06:06.783, 所以scheduleAtFixedRate最大的特點是保證了總時間段內的執行次數)
進一步理解
我們再通過一些問題來幫助你更深入理解Timer實現方式。
schedule 和 scheduleAtFixedRate 有何區別?
- schedule:每次執行完當前任務后,然后間隔一個period的時間再執行下一個任務; 當某個任務執行周期大于時間間隔時,依然按照間隔時間執行下個任務,即它保證了任務之間執行的間隔。
- scheduleAtFixedRate:每次執行時間為上一次任務開始起向后推一個period間隔,也就是說下次執行時間相對于上一次任務開始的時間點;按照上述的例子,它保證了總時間段內的任務的執行次數
為什么幾乎很少使用Timer這種方式?
Timer底層是使用一個單線來實現多個Timer任務處理的,所有任務都是由同一個線程來調度,所有任務都是串行執行,意味著同一時間只能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之后的任務。
如果有一個定時任務在運行時,產生未處理的異常,那么當前這個線程就會停止,那么所有的定時任務都會停止,受到影響。
PS:在這點上你可以看到,定時任務Job中異常和超時等一般都是要自行處理的,以防止對其它任務的影響。