使用springboot時,解決@Scheduled定時器遇到的問題
@Scheduled定時器遇到的問題
@Scheduled 這個注解確實(shí)給我們帶了很大的方便,我們只要加上該注解,并且根據(jù)需求設(shè)置好就可以使用定時任務(wù)了。
但是,我們需要注意的是,@Scheduled 并不一定一定會按時執(zhí)行。
因?yàn)槭褂聾Scheduled 的定時任務(wù)雖然是異步執(zhí)行的,但是,不同的定時任務(wù)之間并不是并行的!?。。。。。?!
在其中一個定時任務(wù)沒有執(zhí)行完之前,其他的定時任務(wù)即使是到了執(zhí)行時間,也是不會執(zhí)行的,它們會進(jìn)行排隊。
也就是如果你想你不同的定時任務(wù)互不影響,到時間就會執(zhí)行,那么你最好將你的定時任務(wù)方法自己搞成異步方法,這樣,
定時任務(wù)其實(shí)就相當(dāng)于調(diào)用了一個線程執(zhí)行任務(wù),一瞬間就結(jié)束了。當(dāng)然,也可以勉強(qiáng)當(dāng)做是任務(wù)都會定時執(zhí)行。
下面說一下@Scheduled 注解的幾個參數(shù)
一、可以通過配置文件配置進(jìn)來的
使用表達(dá)式,該表達(dá)式表示每一秒中執(zhí)行一次。如果上一次方法超過了定時時間還沒有執(zhí)行完,那么下一次定時不會執(zhí)行,
直到上次方法執(zhí)行完后,就會立即執(zhí)行下一次的定時任務(wù)
該方式在工程啟動的時候,并不會立即執(zhí)行,會按照定時表達(dá)式的規(guī)律進(jìn)行執(zhí)行。例如這里,就是1秒后才會執(zhí)行。
@Scheduled(cron="0/1 * * * * ?")
使用固定速率。該表達(dá)式表示每隔一秒鐘執(zhí)行一次。如果上一次方法超過了1秒鐘還沒執(zhí)行完,下一次任務(wù)也不會執(zhí)行,直到
上次方法執(zhí)行完,下次的定時就會立即執(zhí)行。
該方式在工程啟動的時候,會立即執(zhí)行,接下來會按規(guī)律進(jìn)行執(zhí)行。
@Scheduled(fixedRateString="1000") // 單位:毫秒
該方式和上一個的不同在于多加了一個參數(shù),這個參數(shù)是一個初始化參數(shù)。
加上initialDelayString后,在剛啟動的時候,就不會立即執(zhí)行了,而是會等到10秒之后才會執(zhí)行,即使fixedRateString才
1秒鐘。也會在10秒后才會第一次執(zhí)行。
注意:initialDelayString不能喝cron組合使用。
@Scheduled(fixedRateString="1000",initialDelayString="10000")
使用固定延遲。該表達(dá)式表示每次執(zhí)行完后一秒再次執(zhí)行。每一次執(zhí)行,無論執(zhí)行多長時間,下一次執(zhí)行都會在上一次方法
執(zhí)行完后,再過一秒鐘,再次執(zhí)行。
該方式在工程啟動的時候,會立即執(zhí)行,接下來會按規(guī)律進(jìn)行執(zhí)行。
@Scheduled(fixedDelayString="1000")
二、不可通過配置文件配置的 (作用相同)
@Scheduled(fixedRate=1000) // 它們都是接受一個long類型的參數(shù) @Scheduled(fixedDelay=1000) @Scheduled(fixedRate=1000,initialDelay=10000)
這里粘貼一下cron表達(dá)式的規(guī)則:
字段 | 允許值 | 允許的特殊字符 |
分 | 0-59 | , - * / |
小時 | 0-23 | , - * / |
日期 | 1-31 | , - * ? / L W C |
月份 | 1-12 或者 JAN-DEC | , - * / |
星期 | 1-7 或者 SUN-SAT | , - * ? / L C # |
年(可選) | 留空, 1970-2099 | , - * / |
定時任務(wù)@Scheduled使用的那些坑
@Scheduled是spring自帶的注解,默認(rèn)是單線程,常用作定時任務(wù)使用,但是如果是集群版的機(jī)器的話,就考慮加上分布式鎖或者使用分布式定時任務(wù)代替。
一、使用的那些坑?
1.單線程
因?yàn)锧Scheduled默認(rèn)使用的是單線程,如果有兩個任務(wù)A和B,那么任務(wù)A要是阻塞了,任務(wù)B就無法執(zhí)行。
2.@Async和@EnableAsync
為了解決單線程帶來的線程阻塞問題,我們可以使用@Async和@EnableAsync兩個注解采用異步的方式去處理,這樣就是不同的線程去執(zhí)行,但是這種方式也帶來一個新的問題,那就是如果任務(wù)A的任務(wù)執(zhí)行時間>任務(wù)調(diào)度周期時間的話,就會發(fā)生上一個任務(wù)未執(zhí)行完畢,下一個任務(wù)又開始執(zhí)行的邏輯,這種也是有風(fēng)險的。所以最好使用多線程方式,自己控制線程池的數(shù)量,線程名稱等。
二、使用多線程
需要實(shí)現(xiàn)SchedulingConfigurer接口,然后自定義線程池,這樣凡是用到@Scheduled注解的都可以用該線程池,同時也解決了上述比較坑的兩個問題。
@Configuration @EnableScheduling public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); } @Bean public Executor taskExecutor() { return Executors.newScheduledThreadPool(20, (Runnable r) -> { Thread thread = new Thread(r); thread.setName("自定義線程名稱"); return thread; }); } }
以上這一行
thread.setName(“自定義線程名稱”);
是設(shè)置線程名稱,可以通過Thread.currentThread().getName()拿到該名稱,便于在日志中進(jìn)行排查問題。
小結(jié)一下
集群版本如果要使用@Scheduled的話,需要加分布式鎖來控制,或者直接用分布式定時任務(wù)Elasticjob或者xxl-job等。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java設(shè)計模式之命令模式(Command模式)介紹
這篇文章主要介紹了Java設(shè)計模式之命令模式(Command模式)介紹,本文講解了Command模式的定義、如何使用命令模式等內(nèi)容,需要的朋友可以參考下2015-03-03IDEA下使用MyBatisCodeHelper插件的方法詳解
這篇文章主要介紹了IDEA下使用MyBatisCodeHelper插件的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09MyBatis項(xiàng)目的創(chuàng)建和增刪查改操作詳解
這篇文章主要介紹了MyBatis項(xiàng)目的創(chuàng)建和增刪查改操作,文中通過代碼示例和圖文結(jié)合的方式給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-11-11idea中啟動項(xiàng)目彈出 IDEA out of memory窗口的解決方案
這篇文章主要介紹了idea中啟動項(xiàng)目彈出 IDEA out of memory窗口的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01Spring?Boot?中的?@DateTimeFormat?和?@JsonFormat?的用法及作用詳解
本文介紹了SpringBoot中的@DateTimeFormat和@JsonFormat注解的用法,解釋了它們在處理日期和時間數(shù)據(jù)時的作用,并通過實(shí)例代碼展示了如何在REST控制器中使用這些注解,感興趣的朋友跟隨小編一起看看吧2024-11-11