SpringBoot中使用@scheduled定時執行任務需要注意的坑

要注意什麼坑

不繞彎子了,直接說這個坑是啥:

SpringBoot使用@scheduled定時執行任務的時候是在一個單線程中,如果有多個任務,其中一個任務執行時間過長,則有可能會導致其他後續任務被阻塞直到該任務執行完成。也就是會造成一些任務無法定時執行的錯覺

可以通過如下代碼進行測試:

    @Scheduled(cron = "0/1 * * * * ? ")
    public void deleteFile() throws InterruptedException {
        log.info("111delete success, time:" + new Date().toString());
        Thread.sleep(1000 * 5);//模擬長時間執行,比如IO操作,http請求
    }

    @Scheduled(cron = "0/1 * * * * ? ")
    public void syncFile() {
        log.info("222sync success, time:" + new Date().toString());
    }
    
/**輸出如下:
[pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:13 CST 2018
[pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:18 CST 2018
[pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:19 CST 2018
[pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:24 CST 2018
[pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:25 CST 2018
[pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:25 CST 2018
上面的日誌中可以明顯的看到syncFile被阻塞了,直達deleteFile執行完它才執行了
而且從日誌信息中也可以看出@Scheduled是使用了一個線程池中的一個單線程來執行所有任務的。
**/

/**如果把Thread.sleep(1000*5)註釋了,輸出如下:
[pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:04 CST 2018
[pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:04 CST 2018
[pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:05 CST 2018
[pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:05 CST 2018
[pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:06 CST 2018
[pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:06 CST 2018
這下正常了
**/

解決辦法

1.將@Scheduled註釋的方法內部改成異步執行
如下:

//當然了,構建一個合理的線程池也是一個關鍵,否則提交的任務也會在自己構建的線程池中阻塞
    ExecutorService service = Executors.newFixedThreadPool(5);

    @Scheduled(cron = "0/1 * * * * ? ")
    public void deleteFile() {
        service.execute(() -> {
            log.info("111delete success, time:" + new Date().toString());
            try {
                Thread.sleep(1000 * 5);//改成異步執行後,就算你再耗時也不會印象到後續任務的定時調度了
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    @Scheduled(cron = "0/1 * * * * ? ")
    public void syncFile() {
        service.execute(()->{
            log.info("222sync success, time:" + new Date().toString());
        });
    }

2.把Scheduled配置成成多線程執行

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //當然了,這裏設置的線程池是corePoolSize也是很關鍵了,自己根據業務需求設定
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
        
        
        /**爲什麼這麼說呢?
        假設你有4個任務需要每隔1秒執行,而其中三個都是比較耗時的操作可能需要10多秒,而你上面的語句是這樣寫的:
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(3));
        那麼仍然可能導致最後一個任務被阻塞不能定時執行
        **/
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章