@Scheduled注解不能同時執(zhí)行多個定時任務的解決方案
@Scheduled注解不能同時執(zhí)行多個定時任務
最近在使用定時任務的時候發(fā)現(xiàn),自己寫的定時任務沒有執(zhí)行,后來查了上網(wǎng)查了一下,才知道@Scheduled注解的定時任務是單線程的,同一時間段內(nèi)只能執(zhí)行一個定時任務,其它定時任務不執(zhí)行。
需要配置@Scheduled多線程支持,才能實現(xiàn)同一時間段內(nèi),執(zhí)行多個定時任務。
一般情況下面兩個定時任務只會執(zhí)行第一個定時任務,第二個定時任務不會執(zhí)行。
/** ? ? ?* 測試定時任務1 每天22:00:00執(zhí)行 ? ? ?*/ ? ? @Scheduled(cron = "0 0 22 * * ?") ? ? public void test() { ? ? ? ? ? for (int i = 0; i < 20; i++) { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? Thread.sleep(1000 * 10); ? ? ? ? ? ? } catch (InterruptedException e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? ? ? System.out.println("=======================測試定時任務執(zhí)行1======================="); ? ? ? ? } ? ? } ? ? ? /** ? ? ?* 測試定時任務2 每天22:10:00執(zhí)行 ? ? ?*/ ? ? @Scheduled(cron = "0 10 22 * * ?") ? ? public void test2() { ? ? ? ? ? for (int i = 0; i < 20; i++) { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? Thread.sleep(1000 * 10); ? ? ? ? ? ? } catch (InterruptedException e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? ? ? System.out.println("=======================測試定時任務執(zhí)行2======================="); ? ? ? ? } ? ? }
要解決上訴問題,就需要配置 @Scheduled多線程支持,添加一個配置類,代碼如下:
/** ?* @description: 使@schedule支持多線程的配置類 ?* @author: David Allen ?* @create: 2020-12-08 ?**/ @Configuration public class ScheduleConfig implements SchedulingConfigurer { ? ? ? @Override ? ? public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { ? ? ? ? ? Method[] methods = Job.class.getMethods(); ? ? ? ? int defaultPoolSize = 3; ? ? ? ? int corePoolSize = 0; ? ? ? ? ? if (!CollectionUtils.isEmpty(Arrays.asList(methods))) { ? ? ? ? ? ? ? for (Method method : methods) { ? ? ? ? ? ? ? ? ? Scheduled annotation = method.getAnnotation(Scheduled.class); ? ? ? ? ? ? ? ? ? if (annotation != null) { ? ? ? ? ? ? ? ? ? ? ? corePoolSize++; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if (defaultPoolSize > corePoolSize) { ? ? ? ? ? ? ? ? ? corePoolSize = defaultPoolSize; ? ? ? ? ? ? } ? ? ? ? ? ? ? taskRegistrar.setScheduler(Executors.newScheduledThreadPool(corePoolSize)); ? ? ? ? } ? ? } }
@Scheduled同時執(zhí)行多個定時任務所導致的并發(fā)問題
@Scheduled的執(zhí)行順序
@Scheduled注解會在默認情況下以單線程的方式執(zhí)行定時任務。
這個“單線程”指兩個方面:
- 如果一個定時任務執(zhí)行時間大于其任務間隔時間,那么下一次將會等待上一次執(zhí)行結束后再繼續(xù)執(zhí)行。
- 如果多個定時任務在同一時刻執(zhí)行,任務會依次執(zhí)行。
那么這種效果肯定不是我們想要的,為了使@Scheduled效率更高,我們可以通過兩種方法將定時任務變成多線程執(zhí)行:
1、在啟動類中配置TaskScheduler線程池大小
@Bean public TaskScheduler taskScheduler() { ?? ?ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); ?? ?taskScheduler.setPoolSize(50); ?? ?return taskScheduler; }
2、利用Spring提供的@Async注解和@EnableAsync注解
@Component @EnableAsync public class TimedTask{ ? ? @Async ? ? @Scheduled(cron = "0 0/1 * * * ?")//每一分鐘執(zhí)行一次 ? ? public void taskA() { ? ? ? ? //執(zhí)行你的業(yè)務邏輯 ? ? } ? ?? ? ? @Async ? ? @Scheduled(cron = "0 0/1 * * * ?")//每一分鐘執(zhí)行一次 ? ? public void taskB() { ? ? ? ? //執(zhí)行你的業(yè)務邏輯 ? ? }
通過以上方式,定時任務將會以多線程的方式開始執(zhí)行,減小了程序耦合度,提升運行效率。
@Scheduled同步
定時任務在同一時刻開始執(zhí)行有兩種情況:
- 多個任務的間隔時間相同,如都是1分鐘執(zhí)行一次,那么每一分鐘,這些任務都會一起執(zhí)行。
- 多個任務的間隔時間不同,但有重合的時刻。如一個任務每天零點執(zhí)行,另一個任務每一分鐘執(zhí)行,那么這兩個任務在零點的時刻會一起執(zhí)行。
由于任務都是異步的,如果多個任務同時操作同一資源,那么必然會導致錯誤。
這個時候可以給任務加鎖,保證任務互不干擾,擁有在同一時刻執(zhí)行的線程安全:
@Component @EnableAsync public class TimedTask{ ?? ?private Object lock = new Object(); ?? ? ? ? @Async ? ? @Scheduled(cron = "0 0 0 * * ?")//每天零點執(zhí)行 ? ? public void taskA() { ? ? ?? ?synchronized(lock){ ? ? ? ??? ??? ?//執(zhí)行你的業(yè)務邏輯 ?? ??? ?} ? ? } ? ?? ? ? @Async ? ? @Scheduled(cron = "0 0/1 * * * ?")//每一分鐘執(zhí)行一次 ? ? public void taskB() { ? ? ?? ?synchronized(lock){ ? ? ? ??? ??? ?//執(zhí)行你的業(yè)務邏輯 ?? ??? ?} ? ? }
控制定時任務的執(zhí)行順序
如果對執(zhí)行順序有要求的定時任務,有如下兩種情況:
1、在某一時刻同時執(zhí)行
如任務A在零點初始化數(shù)據(jù),任務B每分鐘更新數(shù)據(jù)。那么在零點,必須是任務A先執(zhí)行,其次才是B。但加鎖只能保證線程安全,不能保證執(zhí)行順序。在這種情況下,我們可以借助redis設置獲取鎖的順序,亦或標志字進行執(zhí)行順序的判斷。
2、總是同時執(zhí)行
在任務間隔相同的情況下,一般為業(yè)務的解耦,不應操作共享資源,應當放至同一個定時任務中執(zhí)行。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
解決mybatis-plus3.4.1分頁插件PaginationInterceptor和防止全表更新與刪除插件SqlE
這篇文章給大家介紹了在Spring.xml文件中配置mybatis-plus3.4.1分頁插件PaginationInterceptor和防止全表更新與刪除插件SqlExplainInterceptor過時失效問題解決方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-12-12學會IDEA REST Client后就可以丟掉postman了
這篇文章主要介紹了學會IDEA REST Client后就可以丟掉postman了,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12@ConfigurationProperties在IDEA中出現(xiàn)紅色波浪線問題解決方法
本文介紹了在Springboot項目中,當@ConfigurationProperties注解出現(xiàn)紅色波浪線時的解決方法,文中有詳細的解決方案供大家參考,需要的朋友可以參考下2024-09-09springboot下添加全局異常處理和自定義異常處理的過程解析
在spring項目中,優(yōu)雅處理異常,好處是可以將系統(tǒng)產(chǎn)生的全部異常統(tǒng)一捕獲處理,自定義的異常也由全局異常來捕獲,如果涉及到validator參數(shù)校驗器使用全局異常捕獲也是較為方便,這篇文章主要介紹了springboot下添加全局異常處理和自定義異常處理,需要的朋友可以參考下2023-12-12