Springboot定時任務(wù)Scheduled重復(fù)執(zhí)行操作
今天用scheduled寫定時任務(wù)的時候發(fā)現(xiàn)定時任務(wù)一秒重復(fù)執(zhí)行一次,而我的cron表達(dá)式為 * 0/2 * * * * 。
在源碼調(diào)試的過程中,發(fā)現(xiàn)是我的定時任務(wù)執(zhí)行過程太短導(dǎo)致的。
于是我另外寫了個簡單的定時任務(wù)
@Component
public class TestJob {
@Scheduled(cron = "* 0/2 * * * *")
public void test() {
System.out.println("測試開始");
System.out.println("測試結(jié)束");
}
}
上述任務(wù)在啟動之后一直執(zhí)行。
然后我在任務(wù)后面加入線程睡眠1分鐘。
@Component
public class TestJob {
@Scheduled(cron = "* 0/2 * * * *")
public void test() {
System.out.println("測試開始");
System.out.println("測試結(jié)束");
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡眠結(jié)束");
}
}
上述任務(wù)執(zhí)行一次就沒有再執(zhí)行了。
所以我繼續(xù)深入查看源碼,發(fā)現(xiàn)問題在于CronSequenceGenerator.class的next方法。
public Date next(Date date) {
Calendar calendar = new GregorianCalendar();
calendar.setTimeZone(this.timeZone);
calendar.setTime(date);
//1.設(shè)置下次執(zhí)行時間的毫秒為0,如上次任務(wù)執(zhí)行過程不足1秒,則calendar的時間會被設(shè)置成上次任務(wù)的執(zhí)行時間
calendar.set(14, 0);
long originalTimestamp = calendar.getTimeInMillis();
this.doNext(calendar, calendar.get(1));
//2.由于有上面一步,執(zhí)行時間太短,會導(dǎo)致下述條件為true
if(calendar.getTimeInMillis() == originalTimestamp) {
//3.calendar在原來的時間上增加1秒
calendar.add(13, 1);
//CronSequenceGenerator的doNext算法從指定時間開始(包括指定時間)查找符合cron表達(dá)式規(guī)則下一個匹配的時間
//注意第一個匹配符是*,由于增加了1秒,依然符合cron="* 0/2 * * * *",所以下一個執(zhí)行時間就是在原來的基礎(chǔ)上增加了一秒
this.doNext(calendar, calendar.get(1));
}
return calendar.getTime();
}
請查看代碼中的注釋,由于任務(wù)執(zhí)行時間太短了,代碼會進(jìn)入if語句,并設(shè)置執(zhí)行時間在原來的基礎(chǔ)上增加一秒。
但由于增加一秒后的時間戳依然符合cron表達(dá)式,于是在執(zhí)行完代碼后一秒,任務(wù)又開始執(zhí)行了。
解決辦法:
程序執(zhí)行時間太短沒有關(guān)系,只要cron表達(dá)式秒的匹配符不設(shè)置為*就可以了。
cron表達(dá)式可以設(shè)置為"0 0/2 * * * *",這樣在執(zhí)行到next方法中的doNext方法時就會發(fā)現(xiàn)時間增加1秒不符合cron表達(dá)式了,從而去尋找下一個合適的執(zhí)行時間。
補(bǔ)充知識:SpringBoot 定時器/定時任務(wù):在一個指定的周期時間內(nèi),執(zhí)行某一項(xiàng)任務(wù)。
說多都是累,直接上代碼:
實(shí)現(xiàn)方式有三種(可能還有更多的實(shí)現(xiàn),這三種只是樓主目前所知道的):
1、靜態(tài)定時器,無法修改周期
@Configuration("myScheduled")
@EnableScheduling
public class ScheduledDemo{
//每10秒執(zhí)行一次
@Scheduled(cron="0/10 * * * * ?")
public void timmer(){
System.out.println("執(zhí)行任務(wù)");
}
}
2、動態(tài)定時器
//通過setCron 方法修改 任務(wù)周期
@Component("myScheduled")
public class ScheduledDemo implements SchedulingConfigurer{
//默認(rèn)的任務(wù)周期為 10秒
private String cron = "0/10 * * * * ?";
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// TODO Auto-generated method stub
taskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("執(zhí)行任務(wù)");
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
});
}
public void setCron(String cron) {
this.cron = cron;
}
}
3、動態(tài)定時器:多線程定時任務(wù)執(zhí)行,可以設(shè)置執(zhí)行線程池數(shù)(默認(rèn)一個線程)
@Component("myScheduled")
public class ScheduledImpl{
private ScheduledFuture<?> future;
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
@Override
public void setCron(final String cron) {
stopCron();
future = threadPoolTaskScheduler.schedule(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("執(zhí)行任務(wù)");
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
if(cron==null || "".equals(cron)) {
return null;
}
CronTrigger cronTrigger = new CronTrigger(cron);
return cronTrigger.nextExecutionTime(triggerContext);
}
});
}
@Override
public void stopCron() {
if(future!=null) {
future.cancel(true);
}
}
}
cron 的參數(shù)說明,詳細(xì)說明直接網(wǎng)上搜吧!
大概說明:秒 分 時 日 月 星期 年(可省略)
/ 后面表示周期
- 表示范圍
星期一般用 ? ,為了防止和 日 混淆,如果星期有值,則日用 ?
“0/10 * * * * ?”表示每10秒
“0 0/10 * * * ?”表示每10分
以上這篇Springboot定時任務(wù)Scheduled重復(fù)執(zhí)行操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
- spring 定時任務(wù)@Scheduled詳解
- 詳解SpringBoot 創(chuàng)建定時任務(wù)(配合數(shù)據(jù)庫動態(tài)執(zhí)行)
- SpringBoot實(shí)現(xiàn)動態(tài)定時任務(wù)
- SpringBoot中實(shí)現(xiàn)定時任務(wù)的4種方式詳解
- Spring內(nèi)置定時任務(wù)調(diào)度@Scheduled使用詳解
- SpringBoot 定時任務(wù)遇到的坑
- springboot集成schedule實(shí)現(xiàn)定時任務(wù)
- Spring定時任務(wù)之fixedRateString的實(shí)現(xiàn)示例
相關(guān)文章
Mybatis實(shí)現(xiàn)傳入多個參數(shù)的四種方法詳細(xì)講解
這篇文章主要介紹了Mybatis實(shí)現(xiàn)傳入多個參數(shù)的四種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01
Java List<JSONObject>如何轉(zhuǎn)換為List<實(shí)體類>
這篇文章主要介紹了Java List<JSONObject>如何轉(zhuǎn)換為List<實(shí)體類>的方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05
springboot Interceptor攔截器excludePathPatterns忽略失效
這篇文章主要介紹了springboot Interceptor攔截器excludePathPatterns忽略失效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
springboot對接支付寶支付接口(詳細(xì)開發(fā)步驟總結(jié))
這篇文章主要介紹了springboot對接支付寶支付接口(詳細(xì)開發(fā)步驟總結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Java Comparator.comparing比較導(dǎo)致空指針異常的解決
這篇文章主要介紹了Java Comparator.comparing比較導(dǎo)致空指針異常的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Spring內(nèi)部bean和級聯(lián)屬性用法詳解
這篇文章主要介紹了Java內(nèi)部bean和級聯(lián)屬性用法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10

