在我們現在的項目中,或多或少的都會涉及到定時任務,Spring在3.0之后引入了SpringSchedule,這讓我們在使用Spring的時候,可以很容易的整合SpringSchedule.但是好用歸好用,用的時候還是有一些點注意的.
SpringSchedule 中提供的定時任務,默認是單線程執行的,也就是說如果任務較多,或者某一個任務執行耗時比較久,那么顯然易見,會很容易導致其余任務排隊以及阻塞.
既然存在這種問題,那么怎么去避免這種問題?這時候大家很容易想到的就是使用線程池,多個線程去跑定時任務.沒錯,正確的解決方案就是配置線程池.
之所以默認是單線程執行的,是因為當我們沒有配置taskSchedule的時候,默認創建的是一個單線程的線程池。具體代碼解析參考:https://blog.csdn.net/weixin_40318210/article/details/78149692
先看一下沒配置線程池的情況下的任務執行線程日志:
定時任務業務類的代碼如下:
@Component public class TaskConfig { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); @Scheduled(fixedDelayString = "5000") //單機 public void getTask1() throws InterruptedException { //競爭鎖邏輯代碼 ..... System.out.println("任務1,當前時間:" + dateFormat.format(new Date())+",線程號:"+Thread.currentThread().getName()); //throw new RuntimeException("xxxxx"); Thread.sleep(10000); } @Scheduled(cron = "0/5 * * * * ?") public void getTask2() { System.out.println("任務2,當前時間:" + dateFormat.format(new Date())+",線程號:"+Thread.currentThread().getName()); } }
任務執行日志為:
可以看到執行這兩個任務的線程總是同一個線程.
那么我們現在加入線程池的配置,配置代碼如下:
@Configuration public class ScheduleConfig implements SchedulingConfigurer { public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); } //配置線程池---觸發器和任務共用的 @Bean(destroyMethod="shutdown") public Executor taskExecutor() { return Executors.newScheduledThreadPool(10); } }
接下來我們再觀察一下定時任務的執行信息:
現在看到是加入線程池后,每次執行的定時任務的線程在不斷的變化,同時這兩個任務也可以並行的執行,可以避免任務的阻塞與排隊.
如果你的代碼中使用了SpringSchedule,而且還沒有使用線程池,那么趕緊去修改吧.