@Scheduled注解不能同時(shí)執(zhí)行多個(gè)定時(shí)任務(wù)的解決方案
@Scheduled注解不能同時(shí)執(zhí)行多個(gè)定時(shí)任務(wù)
最近在使用定時(shí)任務(wù)的時(shí)候發(fā)現(xiàn),自己寫的定時(shí)任務(wù)沒有執(zhí)行,后來查了上網(wǎng)查了一下,才知道@Scheduled注解的定時(shí)任務(wù)是單線程的,同一時(shí)間段內(nèi)只能執(zhí)行一個(gè)定時(shí)任務(wù),其它定時(shí)任務(wù)不執(zhí)行。
需要配置@Scheduled多線程支持,才能實(shí)現(xiàn)同一時(shí)間段內(nèi),執(zhí)行多個(gè)定時(shí)任務(wù)。
一般情況下面兩個(gè)定時(shí)任務(wù)只會(huì)執(zhí)行第一個(gè)定時(shí)任務(wù),第二個(gè)定時(shí)任務(wù)不會(huì)執(zhí)行。
/**
? ? ?* 測試定時(shí)任務(wù)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("=======================測試定時(shí)任務(wù)執(zhí)行1=======================");
? ? ? ? }
? ? }
?
? ? /**
? ? ?* 測試定時(shí)任務(wù)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("=======================測試定時(shí)任務(wù)執(zhí)行2=======================");
? ? ? ? }
? ? }要解決上訴問題,就需要配置 @Scheduled多線程支持,添加一個(gè)配置類,代碼如下:
/**
?* @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同時(shí)執(zhí)行多個(gè)定時(shí)任務(wù)所導(dǎo)致的并發(fā)問題
@Scheduled的執(zhí)行順序
@Scheduled注解會(huì)在默認(rèn)情況下以單線程的方式執(zhí)行定時(shí)任務(wù)。
這個(gè)“單線程”指兩個(gè)方面:
- 如果一個(gè)定時(shí)任務(wù)執(zhí)行時(shí)間大于其任務(wù)間隔時(shí)間,那么下一次將會(huì)等待上一次執(zhí)行結(jié)束后再繼續(xù)執(zhí)行。
- 如果多個(gè)定時(shí)任務(wù)在同一時(shí)刻執(zhí)行,任務(wù)會(huì)依次執(zhí)行。
那么這種效果肯定不是我們想要的,為了使@Scheduled效率更高,我們可以通過兩種方法將定時(shí)任務(wù)變成多線程執(zhí)行:
1、在啟動(dòng)類中配置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è)務(wù)邏輯
? ? }
? ??
? ? @Async
? ? @Scheduled(cron = "0 0/1 * * * ?")//每一分鐘執(zhí)行一次
? ? public void taskB() {
? ? ? ? //執(zhí)行你的業(yè)務(wù)邏輯
? ? }通過以上方式,定時(shí)任務(wù)將會(huì)以多線程的方式開始執(zhí)行,減小了程序耦合度,提升運(yùn)行效率。
@Scheduled同步
定時(shí)任務(wù)在同一時(shí)刻開始執(zhí)行有兩種情況:
- 多個(gè)任務(wù)的間隔時(shí)間相同,如都是1分鐘執(zhí)行一次,那么每一分鐘,這些任務(wù)都會(huì)一起執(zhí)行。
- 多個(gè)任務(wù)的間隔時(shí)間不同,但有重合的時(shí)刻。如一個(gè)任務(wù)每天零點(diǎn)執(zhí)行,另一個(gè)任務(wù)每一分鐘執(zhí)行,那么這兩個(gè)任務(wù)在零點(diǎn)的時(shí)刻會(huì)一起執(zhí)行。
由于任務(wù)都是異步的,如果多個(gè)任務(wù)同時(shí)操作同一資源,那么必然會(huì)導(dǎo)致錯(cuò)誤。
這個(gè)時(shí)候可以給任務(wù)加鎖,保證任務(wù)互不干擾,擁有在同一時(shí)刻執(zhí)行的線程安全:
@Component
@EnableAsync
public class TimedTask{
?? ?private Object lock = new Object();
?? ?
? ? @Async
? ? @Scheduled(cron = "0 0 0 * * ?")//每天零點(diǎn)執(zhí)行
? ? public void taskA() {
? ? ?? ?synchronized(lock){
? ? ? ??? ??? ?//執(zhí)行你的業(yè)務(wù)邏輯
?? ??? ?}
? ? }
? ??
? ? @Async
? ? @Scheduled(cron = "0 0/1 * * * ?")//每一分鐘執(zhí)行一次
? ? public void taskB() {
? ? ?? ?synchronized(lock){
? ? ? ??? ??? ?//執(zhí)行你的業(yè)務(wù)邏輯
?? ??? ?}
? ? }控制定時(shí)任務(wù)的執(zhí)行順序
如果對(duì)執(zhí)行順序有要求的定時(shí)任務(wù),有如下兩種情況:
1、在某一時(shí)刻同時(shí)執(zhí)行
如任務(wù)A在零點(diǎn)初始化數(shù)據(jù),任務(wù)B每分鐘更新數(shù)據(jù)。那么在零點(diǎn),必須是任務(wù)A先執(zhí)行,其次才是B。但加鎖只能保證線程安全,不能保證執(zhí)行順序。在這種情況下,我們可以借助redis設(shè)置獲取鎖的順序,亦或標(biāo)志字進(jìn)行執(zhí)行順序的判斷。
2、總是同時(shí)執(zhí)行
在任務(wù)間隔相同的情況下,一般為業(yè)務(wù)的解耦,不應(yīng)操作共享資源,應(yīng)當(dāng)放至同一個(gè)定時(shí)任務(wù)中執(zhí)行。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)基于UDP協(xié)議的聊天小程序操作
UDP是與TCP相對(duì)應(yīng)的協(xié)議,UDP適用于一次只傳送少量數(shù)據(jù)、對(duì)可靠性要求不高的應(yīng)用環(huán)境。正因?yàn)閁DP協(xié)議沒有連接的過程,所以它的通信效率高;但也正因?yàn)槿绱?,它的可靠性不如TCP協(xié)議高,本文給大家介紹java實(shí)現(xiàn)基于UDP協(xié)議的聊天小程序操作,感興趣的朋友一起看看吧2021-10-10
java數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列示例介紹
大家好,本篇文章主要講的是java數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列示例介紹,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01
Mybatis-Plus默認(rèn)主鍵策略導(dǎo)致自動(dòng)生成19位長度主鍵id的坑
這篇文章主要介紹了Mybatis-Plus默認(rèn)主鍵策略導(dǎo)致自動(dòng)生成19位長度主鍵id的坑,本文一步步給大家分享解決方法,給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
一篇文章帶你入門java算術(shù)運(yùn)算符(加減乘除余,字符連接)
這篇文章主要介紹了Java基本數(shù)據(jù)類型和運(yùn)算符,結(jié)合實(shí)例形式詳細(xì)分析了java基本數(shù)據(jù)類型、數(shù)據(jù)類型轉(zhuǎn)換、算術(shù)運(yùn)算符、邏輯運(yùn)算符等相關(guān)原理與操作技巧,需要的朋友可以參考下2021-08-08
mybatis-plus數(shù)據(jù)權(quán)限實(shí)現(xiàn)代碼
這篇文章主要介紹了mybatis-plus數(shù)據(jù)權(quán)限實(shí)現(xiàn),結(jié)合了mybatis-plus的插件方式,做出了自己的注解方式的數(shù)據(jù)權(quán)限,雖然可能存在一部分的局限性,但很好的解決了我們自己去解析SQL的功能,需要的朋友可以參考下2023-06-06

