關(guān)于@Scheduled注解的任務(wù)為什么不執(zhí)行的問(wèn)題
概述
在SpringBoot中可以通過(guò)@Scheduled來(lái)注解定義一個(gè)定時(shí)任務(wù),但是有時(shí)候你可能發(fā)現(xiàn)有的定時(shí)任務(wù)道理時(shí)間卻沒(méi)有執(zhí)行,但是又不是每次都不執(zhí)行,為什么呢???
舉例說(shuō)明
下面這段diam定義了一個(gè)沒(méi)隔10s執(zhí)行一次的定時(shí)任務(wù):
package com.study.practice.schedule; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** ?* @Description : schedule測(cè)試 ?* @Version : V1.0.0 ?* @Date : 2021/12/1 11:22 ?*/ @Component @Slf4j public class ScheduleTest { ? ? @Scheduled(cron = "0/10 * * * * ?") ? ? public void execute() { ? ? ? ? log.info("Scheduled task is running ... ..."); ? ? } }
記得在啟動(dòng)類(lèi)或者Configuration類(lèi)上添加了@EnableScheduling注解。
啟動(dòng)應(yīng)用,控制臺(tái)每隔10秒打印一條日志:
2021-12-01 11:27:00.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:10.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:20.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:30.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:40.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:27:50.001 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:28:00.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:28:10.002 INFO 97876 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
但是,問(wèn)題才剛剛開(kāi)始。
在上面的相關(guān)代碼中,我們使用cron表達(dá)式指定的定時(shí)任務(wù)執(zhí)行時(shí)間點(diǎn)從0秒開(kāi)始,每隔10s執(zhí)行一次,現(xiàn)在我們?cè)偌右粋€(gè)定時(shí)任務(wù):
package com.study.practice.schedule; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /** ?* @Description : schedule測(cè)試 ?* @Version : V1.0.0 ?* @Date : 2021/12/1 11:22 ?*/ @Component @Slf4j public class ScheduleTestTwo { ? ? @Scheduled(cron = "0/10 * * * * *") ? ? public void second() throws InterruptedException { ? ? ? ? log.info("Second scheduled task is running... ..."); ? ? ? ? TimeUnit.SECONDS.sleep(5); ? ? } }
啟動(dòng)項(xiàng)目,執(zhí)行結(jié)果如下:
2021-12-01 11:36:30.001 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:36:30.002 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:40.002 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:45.002 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:36:50.001 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:36:55.002 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 11:37:00.001 INFO 134576 --- [ scheduling-1] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 11:37:05.001 INFO 134576 --- [ scheduling-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
可以看到任務(wù)1和任務(wù)2本該都是每個(gè)10s執(zhí)行,但是卻發(fā)現(xiàn)只有任務(wù)2執(zhí)行了,任務(wù)1卻等待了5s之后才執(zhí)行。
原因分析
為了找到原因,我們從@Scheduled注解的源碼開(kāi)始分析:
下面是@Scheduled注解的注釋?zhuān)?/p>
?* ?* <p>Processing of {@code @Scheduled} annotations is performed by ?* registering a {@link ScheduledAnnotationBeanPostProcessor}. This can be ?* done manually or, more conveniently, through the {@code <task:annotation-driven/>} ?* element or @{@link EnableScheduling} annotation. ?*
劃重點(diǎn), 每一個(gè)有@Scheduled注解的方法都會(huì)被注冊(cè)為一個(gè)ScheduledAnnotationBeanPostProcessor, 再接著往下看ScheduledAnnotationBeanPostProcessor:
?? ?/** ?? ? * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke ?? ? * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService} ?? ? * to be wrapped as a TaskScheduler. ?? ? * <p>If not specified, default scheduler resolution will apply: searching for a ?? ? * unique {@link TaskScheduler} bean in the context, or for a {@link TaskScheduler} ?? ? * bean named "taskScheduler" otherwise; the same lookup will also be performed for ?? ? * a {@link ScheduledExecutorService} bean. If neither of the two is resolvable, ?? ? * a local single-threaded default scheduler will be created within the registrar. ?? ? * @see #DEFAULT_TASK_SCHEDULER_BEAN_NAME ?? ? */ ?? ?public void setScheduler(Object scheduler) { ?? ??? ?this.scheduler = scheduler; ?? ?}
重點(diǎn)來(lái)了,注意這句話(huà):
if neither of two is resolvable, a local single-thread default scheduler will be created within in the registrar.
這句話(huà)意味著,如果我們不主動(dòng)配置我們需要的TaskScheduler,SpringBoot會(huì)默認(rèn)使用一個(gè)單線(xiàn)程的scheduler來(lái)處理我們用@Scheduled注解實(shí)現(xiàn)的定時(shí)任務(wù),到此我們剛才的問(wèn)題就可以理解了。
因?yàn)槭菃蝹€(gè)線(xiàn)程執(zhí)行所有的定時(shí)任務(wù),所有task2如果先執(zhí)行,因?yàn)閳?zhí)行中等待了5s,所以task2執(zhí)行完后,task1接著繼續(xù)執(zhí)行。
解決方案
搞清楚這個(gè)流程后,解決這個(gè)問(wèn)題就很簡(jiǎn)單了。
根據(jù)剛才注釋的描述,我們只需要提供一個(gè)滿(mǎn)足自己需要的TaskScheduler并注冊(cè)到context容器中就可以了。
package com.study.practice.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; /** ?* @Description : scheduler配置類(lèi) ?* @Version : V1.0.0 ?* @Date : 2021/12/1 14:00 ?*/ @Configuration public class ScheduledTaskConfig implements SchedulingConfigurer { ? ? @Override ? ? public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { ? ? ? ? ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); ? ? ? ? taskScheduler.setPoolSize(2); ? ? ? ? taskScheduler.initialize(); ? ? ? ? taskRegistrar.setTaskScheduler(taskScheduler); ? ? } }
上面的代碼提供了一個(gè)線(xiàn)程池大小為2的taskScheduler,再次啟動(dòng)SpringBoot查看效果。
2021-12-01 14:05:20.001 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 14:05:20.001 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:30.002 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:30.002 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
2021-12-01 14:05:40.001 INFO 39768 --- [TaskScheduler-1] c.study.practice.schedule.ScheduleTest : Scheduled task is running ... ...
2021-12-01 14:05:40.001 INFO 39768 --- [TaskScheduler-2] c.s.practice.schedule.ScheduleTestTwo : Second scheduled task is running... ...
可以看到,當(dāng)線(xiàn)程池里有兩個(gè)線(xiàn)程時(shí),這兩個(gè)任務(wù)各種按照預(yù)定的時(shí)間進(jìn)行觸發(fā),互不影響了。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于Lambda表達(dá)式的方法引用和構(gòu)造器引用簡(jiǎn)的單示例
這篇文章主要介紹了關(guān)于Lambda表達(dá)式的方法引用和構(gòu)造器引用簡(jiǎn)的單示例,方法引用與構(gòu)造器引用可以使?Lambda?表達(dá)式的代碼塊更加簡(jiǎn)潔<BR>,需要的朋友可以參考下2023-04-04ArrayList在for循環(huán)中使用remove方法移除元素方法介紹
這篇文章主要介紹了ArrayList在for循環(huán)中使用remove方法移除元素的內(nèi)容,介紹了具體代碼實(shí)現(xiàn),需要的朋友可以參考下。2017-09-09Java編寫(xiě)程序之輸入一個(gè)數(shù)字實(shí)現(xiàn)該數(shù)字階乘的計(jì)算
這篇文章主要介紹了Java編寫(xiě)程序之輸入一個(gè)數(shù)字實(shí)現(xiàn)該數(shù)字階乘的計(jì)算,本文通過(guò)實(shí)例代碼給大家介紹的非常想詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02Java實(shí)現(xiàn)復(fù)制文件并命名的超簡(jiǎn)潔寫(xiě)法
這篇文章主要介紹了Java實(shí)現(xiàn)復(fù)制文件并命名的超簡(jiǎn)潔寫(xiě)法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-112020最新eclipse安裝過(guò)程及細(xì)節(jié)
這篇文章主要介紹了2020最新eclipse安裝過(guò)程及細(xì)節(jié),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08java數(shù)組、泛型、集合在多態(tài)中的使用及對(duì)比
本文主要介紹了java數(shù)組、泛型、集合在多態(tài)中的使用及對(duì)比。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-03-03mybatis sum(參數(shù)) 列名作為參數(shù)的問(wèn)題
這篇文章主要介紹了mybatis sum(參數(shù)) 列名作為參數(shù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01