springboot定時任務用起來你們應該都會用,加兩註解,加點配置就能夠運行。可是若是僅僅處在應用層面的話,有不少內在的問題開發中可能難以察覺。話很少說,我先用一種極度誇張的手法,描述一下遇到的一個問題。spring
@Component
public class ScheduleTest {
@Scheduled(initialDelay = 1000,fixedRate = 2*1000)
public void test_a(){
System.out.println("123");
}
@Scheduled(initialDelay = 2*1000,fixedRate = 2*1000)
public void test_b(){
while (true){
try {
Thread.sleep(2*1000);
System.out.println("456");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
複製代碼
上面代碼是一個項目中的兩個定時任務,test_a是正常的方法,test_b是發生異常的方法,爲了凸顯異常,我搞了個死循環。springboot
在這種狀況下,使用默認的定時任務配置運行,會發生什麼現象呢?試試看就知道了,定時任務一直在方法b中循環着,方法a永遠執行不到!!!bash
查看源代碼後發現SpringBoot源碼解析-Scheduled定時器的原理,springboot中,默認的定時任務線程池是隻有一個線程的,因此若是在一堆定時任務中,有一個發生了延時或者死循環之類的異常,很大可能會影響到其餘的定時任務。異步
既然問題出在線程池數量上,那麼爲了讓各個任務之間不會互相干擾,那就配置相應的線程池就行了。post
@Scheduled(initialDelay = 1000,fixedRate = 2*1000)
@Async
public void test_a(){
System.out.println("123");
}
@Scheduled(initialDelay = 2*1000,fixedRate = 2*1000)
@Async
public void test_b(){
while (true){
try {
Thread.sleep(2*1000);
System.out.println("456");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製代碼
既然在單線程中由於一個任務卡住而影響到其餘任務,那麼把這個任務異步執行,問題就解決啦。ui
@Configuration
public class GlobalConfiguration {
@Bean
public TaskScheduler schedule(){
return new ConcurrentTaskScheduler(new ScheduledThreadPoolExecutor(2));
}
}
複製代碼
既然單線程會互相干擾,那麼分配足夠的線程,讓他們各自分開運行,也是能夠解決的。spa
兩種方案均可以解決各個任務之間互相干擾的問題,可是須要根據實際狀況選擇合適的。咱們就以上面出現死循環的代碼來分析。線程
若是在定時任務中真的發生了死循環,那麼使用異步執行則會帶來災難性的後果。由於在定時任務這個線程中,每次任務執行完畢後,他會計算下次時間,再次添加一個任務進入異步線程池。而添加進異步線程池的任務由於死循環而一直佔用着線程資源。隨着時間的增長異步線程池的全部線程資源都會被死循環的任務佔據,致使其餘服務所有阻塞。3d
而使用自定義定時任務線程池則會好一點,由於只有當任務執行完成後,纔會計算時間,在執行下次任務。雖然由於死循環任務一直在執行,可是也頂多佔據一個線程的資源,不至於更大範圍的影響。code