帶你3分鐘帶你搞定Spring Boot中Schedule
一、背景介紹
在實(shí)際的業(yè)務(wù)開發(fā)過程中,我們經(jīng)常會(huì)需要定時(shí)任務(wù)來幫助我們完成一些工作,例如每天早上 6 點(diǎn)生成銷售報(bào)表、每晚 23 點(diǎn)清理臟數(shù)據(jù)等等。
如果你當(dāng)前使用的是 SpringBoot 來開發(fā)項(xiàng)目,那么完成這些任務(wù)會(huì)非常容易!
SpringBoot 默認(rèn)已經(jīng)幫我們完成了相關(guān)定時(shí)任務(wù)組件的配置,我們只需要添加相應(yīng)的注解@Scheduled
就可以實(shí)現(xiàn)任務(wù)調(diào)度!
二、方案實(shí)踐
2.1、pom 包配置
pom
包里面只需要引入Spring Boot Starter
包即可!
??????? <dependencies> <!--spring boot核心--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--spring boot 測試--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
???????2.2、啟動(dòng)類啟用定時(shí)調(diào)度
在啟動(dòng)類上面加上@EnableScheduling
即可開啟定時(shí)
@SpringBootApplication @EnableScheduling public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); } }
???????2.3、創(chuàng)建定時(shí)任務(wù)
Spring Scheduler
支持四種形式的任務(wù)調(diào)度!
- fixedRate:固定速率執(zhí)行,例如每5秒執(zhí)行一次
- fixedDelay:固定延遲執(zhí)行,例如距離上一次調(diào)用成功后2秒執(zhí)行
- initialDelay:初始延遲任務(wù),例如任務(wù)開啟過5秒后再執(zhí)行,之后以固定頻率或者間隔執(zhí)行
- cron:使用 Cron 表達(dá)式執(zhí)行定時(shí)任務(wù)
2.3.1、固定速率執(zhí)行
你可以通過使用fixedRate
參數(shù)以固定時(shí)間間隔來執(zhí)行任務(wù),示例如下:
@Component public class SchedulerTask { private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * fixedRate:固定速率執(zhí)行。每5秒執(zhí)行一次。 */ @Scheduled(fixedRate = 5000) public void runWithFixedRate() { log.info("Fixed Rate Task,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date())); } }
運(yùn)行ScheduleApplication
主程序,即可看到控制臺(tái)輸出效果:
Fixed Rate Task,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:00
Fixed Rate Task,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:10
...
???????2.3.2、固定延遲執(zhí)行
你可以通過使用fixedDelay
參數(shù)來設(shè)置上一次任務(wù)調(diào)用完成與下一次任務(wù)調(diào)用開始之間的延遲時(shí)間,示例如下:
@Component public class SchedulerTask { private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * fixedDelay:固定延遲執(zhí)行。距離上一次調(diào)用成功后2秒后再執(zhí)行。 */ @Scheduled(fixedDelay = 2000) public void runWithFixedDelay() { log.info("Fixed Delay Task,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date())); } }
控制臺(tái)輸出效果:
Fixed Delay Task,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:00
Fixed Delay Task,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:02
...
???????2.3.3、初始延遲任務(wù)
你可以通過使用initialDelay
參數(shù)與fixedRate
或者fixedDelay
搭配使用來實(shí)現(xiàn)初始延遲任務(wù)調(diào)度。
@Component public class SchedulerTask { private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * initialDelay:初始延遲。任務(wù)的第一次執(zhí)行將延遲5秒,然后將以5秒的固定間隔執(zhí)行。 */ @Scheduled(initialDelay = 5000, fixedRate = 5000) public void reportCurrentTimeWithInitialDelay() { log.info("Fixed Rate Task with Initial Delay,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date())); } }
控制臺(tái)輸出效果:
Fixed Rate Task with Initial Delay,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:05
Fixed Rate Task with Initial Delay,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:10
...
???????2.3.4、使用 Cron 表達(dá)式
Spring Scheduler
同樣支持Cron
表達(dá)式,如果以上簡單參數(shù)都不能滿足現(xiàn)有的需求,可以使用 cron 表達(dá)式來定時(shí)執(zhí)行任務(wù)。
關(guān)于cron
表達(dá)式的具體用法,可以點(diǎn)擊參考這里: https://cron.qqe2.com/
@Component public class SchedulerTask { private static final Logger log = LoggerFactory.getLogger(SchedulerTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * cron:使用Cron表達(dá)式。每6秒中執(zhí)行一次 */ @Scheduled(cron = "*/6 * * * * ?") public void reportCurrentTimeWithCronExpression() { log.info("Cron Expression,Current Thread : {},The time is now : {}", Thread.currentThread().getName(), dateFormat.format(new Date())); } }
控制臺(tái)輸出效果:
Cron Expression,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:06
Cron Expression,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 11:46:12
...
???????2.4、異步執(zhí)行定時(shí)任務(wù)
在介紹異步執(zhí)行定時(shí)任務(wù)之前,我們先看一個(gè)例子!
在下面的示例中,我們創(chuàng)建了一個(gè)每隔2秒執(zhí)行一次的定時(shí)任務(wù),在任務(wù)里面大概需要花費(fèi) 3 秒鐘,猜猜執(zhí)行結(jié)果如何?
@Component public class AsyncScheduledTask { private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Scheduled(fixedRate = 2000) public void runWithFixedDelay() { try { TimeUnit.SECONDS.sleep(3); log.info("Fixed Delay Task, Current Thread : {} : The time is now {}", Thread.currentThread().getName(), dateFormat.format(new Date())); } catch (InterruptedException e) { log.error("錯(cuò)誤信息",e); } } }
控制臺(tái)輸入結(jié)果:
Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:26
Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:31
Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:36
Fixed Delay Task, Current Thread : scheduling-1 : The time is now 2020-12-15 17:55:41
...
很清晰的看到,任務(wù)調(diào)度頻率變成了每隔5秒調(diào)度一次!
這是為啥呢?
從Current Thread : scheduling-1
輸出結(jié)果可以很看到,任務(wù)執(zhí)行都是同一個(gè)線程!默認(rèn)的情況下,@Scheduled
任務(wù)都在 Spring 創(chuàng)建的大小為 1 的默認(rèn)線程池中執(zhí)行!
更直觀的結(jié)果是,任務(wù)都是串行執(zhí)行!
下面,我們將其改成異步線程來執(zhí)行,看看效果如何?
@Component @EnableAsync public class AsyncScheduledTask { private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Async @Scheduled(fixedDelay = 2000) public void runWithFixedDelay() { try { TimeUnit.SECONDS.sleep(3); log.info("Fixed Delay Task, Current Thread : {} : The time is now {}", Thread.currentThread().getName(), dateFormat.format(new Date())); } catch (InterruptedException e) { log.error("錯(cuò)誤信息",e); } } }
控制臺(tái)輸出結(jié)果:
Fixed Delay Task, Current Thread : SimpleAsyncTaskExecutor-1 : The time is now 2020-12-15 18:55:26
Fixed Delay Task, Current Thread : SimpleAsyncTaskExecutor-2 : The time is now 2020-12-15 18:55:28
Fixed Delay Task, Current Thread : SimpleAsyncTaskExecutor-3 : The time is now 2020-12-15 18:55:30
...
任務(wù)的執(zhí)行頻率不受方法內(nèi)的時(shí)間影響,以并行方式執(zhí)行!
2.5、自定義任務(wù)線程池
雖然默認(rèn)的情況下,@Scheduled
任務(wù)都在 Spring 創(chuàng)建的大小為 1 的默認(rèn)線程池中執(zhí)行,但是我們也可以自定義線程池,只需要實(shí)現(xiàn)SchedulingConfigurer
類即可!
自定義線程池示例如下:
@Configuration public class SchedulerConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); //線程池大小為10 threadPoolTaskScheduler.setPoolSize(10); //設(shè)置線程名稱前綴 threadPoolTaskScheduler.setThreadNamePrefix("scheduled-thread-"); //設(shè)置線程池關(guān)閉的時(shí)候等待所有任務(wù)都完成再繼續(xù)銷毀其他的Bean threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true); //設(shè)置線程池中任務(wù)的等待時(shí)間,如果超過這個(gè)時(shí)候還沒有銷毀就強(qiáng)制銷毀,以確保應(yīng)用最后能夠被關(guān)閉,而不是阻塞住 threadPoolTaskScheduler.setAwaitTerminationSeconds(60); //這里采用了CallerRunsPolicy策略,當(dāng)線程池沒有處理能力的時(shí)候,該策略會(huì)直接在 execute 方法的調(diào)用線程中運(yùn)行被拒絕的任務(wù);如果執(zhí)行程序已關(guān)閉,則會(huì)丟棄該任務(wù) threadPoolTaskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); threadPoolTaskScheduler.initialize(); scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler); } }
我們啟動(dòng)服務(wù),看看cron
任務(wù)示例調(diào)度效果:
Cron Expression,Current Thread : scheduled-thread-1,The time is now : 2020-12-15 20:46:00
Cron Expression,Current Thread : scheduled-thread-2,The time is now : 2020-12-15 20:46:06
Cron Expression,Current Thread : scheduled-thread-3,The time is now : 2020-12-15 20:46:12
Cron Expression,Current Thread : scheduled-thread-4,The time is now : 2020-12-15 20:46:18
....
當(dāng)前線程名稱已經(jīng)被改成自定義scheduled-thread
的前綴!
三、小結(jié)
本文主要圍繞Spring scheduled
應(yīng)用實(shí)踐進(jìn)行分享,如果是單體應(yīng)用,使用SpringBoot
內(nèi)置的@scheduled
注解可以解決大部分業(yè)務(wù)需求,上手非常容易!
項(xiàng)目源代碼地址:spring-boot-example-scheduled
四、參考
1、https://springboot.io/t/topic/2758
到此這篇關(guān)于3分鐘帶你搞定Spring Boot中Schedule 的文章就介紹到這了,更多相關(guān)Spring Boot中Schedule 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot中定時(shí)任務(wù)@Scheduled的多線程使用詳解
- SpringBoot通過@Scheduled實(shí)現(xiàn)定時(shí)任務(wù)及單線程運(yùn)行問題解決
- SpringBoot?ScheduledTaskRegistrar解決動(dòng)態(tài)定時(shí)任務(wù)思路詳解
- SpringBoot定時(shí)任務(wù)動(dòng)態(tài)擴(kuò)展ScheduledTaskRegistrar詳解
- SpringBoot中定時(shí)任務(wù)@Scheduled注解的使用解讀
- 解決SpringBoot中的Scheduled單線程執(zhí)行問題
- SpringBoot中使用@scheduled定時(shí)執(zhí)行任務(wù)的坑
- springboot使用定時(shí)器@Scheduled不管用的解決
相關(guān)文章
Java時(shí)區(qū)轉(zhuǎn)換實(shí)例代碼解析
這篇文章主要介紹了Java時(shí)區(qū)轉(zhuǎn)換實(shí)例代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Java實(shí)現(xiàn)經(jīng)典游戲2048的示例代碼
2014年Gabriele Cirulli利用周末的時(shí)間寫2048這個(gè)游戲的程序。本文將用java語言實(shí)現(xiàn)這一經(jīng)典游戲,并采用了swing技術(shù)進(jìn)行了界面化處理,需要的可以參考一下2022-02-02SpringBoot在IDEA中實(shí)現(xiàn)熱部署(JRebel實(shí)用版)
這篇文章主要介紹了SpringBoot在IDEA中實(shí)現(xiàn)熱部署(JRebel實(shí)用版),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Java數(shù)字轉(zhuǎn)換工具類NumberUtil的使用
NumberUtil是一個(gè)功能強(qiáng)大的Java工具類,用于處理數(shù)字的各種操作,包括數(shù)值運(yùn)算、格式化、隨機(jī)數(shù)生成和數(shù)值判斷,下面就來介紹一下NumberUtil的具體使用,感興趣的可以了解一下2025-02-02SpringBoot 自定義注解異步記錄復(fù)雜日志詳解
這篇文章主要為大家介紹了SpringBoot 自定義注解異步記錄復(fù)雜日志詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09java線程并發(fā)cyclicbarrier類使用示例
CyclicBarrier類似于CountDownLatch也是個(gè)計(jì)數(shù)器,不同的是CyclicBarrier數(shù)的是調(diào)用了CyclicBarrier.await()進(jìn)入等待的線程數(shù),當(dāng)線程數(shù)達(dá)到了CyclicBarrier初始時(shí)規(guī)定的數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù),下面使用示例學(xué)習(xí)他的使用方法2014-01-01解決SpringAop內(nèi)部調(diào)用時(shí)不經(jīng)過代理類的問題
這篇文章主要介紹了解決SpringAop內(nèi)部調(diào)用時(shí)不經(jīng)過代理類的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01