前面一篇博文 《Spring之定時任務基本使用篇》 介紹了Spring環境下,定時任務的簡單使用姿式,也留了一些問題,這一篇則但願能針對這些問題給個答案java
前面一篇博文,拋出了下面的幾個問題,接下來則圍繞問題進行分析linux
若是默認是串行的git
若是是併發執行的github
如何確認一個項目中的多個定時任務是串行執行仍是併發執行呢?要想驗證這個功能,最好的法子就是寫個testcase,好比定義兩個定時任務,在其中一個任務中寫個死循環,看另一個任務是否會正常執行安全
@Scheduled(cron = "0/1 * * * * ?") public void sc1() throws InterruptedException { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); while (true) { Thread.sleep(5000); } } @Scheduled(cron = "0/1 * * * * ?") public void sc2() { System.out.println(Thread.currentThread().getName() + " | sc2 " + System.currentTimeMillis()); }
首先咱們分析的是 sc1和sc2這兩個任務的執行是串行仍是並行的,暫時先不考慮 sc1 調用時阻塞,下一秒是不是開新的線程再調用sc1併發
實際運行,GIF圖演示以下異步
上圖的結果,印證了默認的狀況下,多個定時任務時串行執行的;若是一個任務出現阻塞,其餘的任務都會受到影響async
既然是順序執行的,那麼優先級怎麼定?每次都是固定的,仍是隨機的呢?源碼分析
要驗證上面的方法,也容易,一樣兩個任務,看他們的輸出是否會亂掉,若是每次都是任務1打印完再打印任務2,那就是固定優先級的;不然每次調度時,順序很差說學習
測試代碼以下
@Scheduled(cron = "0/1 * * * * ?") public void sc1() { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); } @Scheduled(cron = "0/1 * * * * ?") public void sc2() { System.out.println(Thread.currentThread().getName() + " | sc2 " + System.currentTimeMillis()); }
實測結果以下
從輸出得出結論:順序是串掉的,並無表現出明顯的優先級關係
接下來的問題就是我但願這些任務能夠併發執行,能夠實現麼?
固然是能夠,用起來也比較簡單,首先是在Application上添加註解@EnableAsync
,開啓異步調用,而後再計劃任務上加上@Async
註解便可,一個簡單的demo以下
@EnableAsync @EnableScheduling @SpringBootApplication public class QuickMediaApplication { public static void main(String[] args) { SpringApplication.run(QuickMediaApplication.class, args); } @Scheduled(cron = "0/1 * * * * ?") @Async public void sc1() { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); } }
上面執行以後,查看輸出(異步調度時,理論上線程名應該不同)
從上面的輸出,能夠簡單的推理,每次調度上面的任務都是新開了一個線程來作的,因此若是在定時任務中寫了死循環,是否會致使無限線程,最後整個進程崩掉?
額外提一句,linux系統下單進程的線程數是有上線的,查看命令爲:
ulimit -u
在測試以前,先看下上面的正常任務執行,以下面的動圖,線程數並無誇張的長法
接下來換成死循環的調度方式,實際測試以下,線程數蹭蹭的上漲
因此使用默認的異步調用方式,並非一個好注意,說不許就被玩死了本身都不知道,那麼能夠用本身的線程池來管理這些異步任務麼?
用自定義的線程池來取代默認線程管理方式,無疑是一個更加安全和靈活的方式,使用起來也並不麻煩,和日常建立線程池的套路沒什麼區別,要在Spring生態中使用,就把它搞成bean便可
直接藉助Spring的線程池ThreadPoolTaskExecutor
@Bean public AsyncTaskExecutor asyncTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("yhh-schedule-"); executor.setMaxPoolSize(10); executor.setCorePoolSize(3); executor.setQueueCapacity(0); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); return executor; } @Scheduled(cron = "0/1 * * * * ?") @Async public void sc1() throws InterruptedException { System.out.println(Thread.currentThread().getName() + " | sc1 " + System.currentTimeMillis()); while (true) { Thread.sleep(1000 * 5); } }
實際演示的結果以下,最多10個線程,再提交的任務直接丟棄
簡單說一下,用自定義線程池的好處:
原本這篇博文在昨天即8月2號就應該寫完的,結果晚上生產環境下除了點問題,解決線上故障以後就比較晚了,留到了今天,哎,拖延症也是要不得。。。
下面小結Spring中定時任務的幾個知識點
@Async
註解可使定時任務異步調度;可是須要開啓配置,在啓動類上添加 @EnableAsync
註解一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
小灰灰Blog&公衆號
知識星球