近期一個項目有個定時任務阻塞住了,從日誌裏看沒有任何異常產生,但就是定時再也不執行了,進程還在,jstack看了下線程處於WAIT狀態,但就是再也不定時觸發。因而拿代碼分析了一下,代碼原理很簡單,拿ScheduledExecutorService.scheduleWithFixedDelay設定的定時任務,簡化後相似這樣:java
public class Application { private static ScheduledExecutorService timer = Executors.newScheduledThreadPool(2); public static void main(String[] args) { timer.scheduleWithFixedDelay(() -> { try { //此處執行任務 } catch(Exception ex) { System.out.println(ex.getMessage()); } }, 0, 1, TimeUnit.SECONDS); } }
通常定時任務掛了,第一考慮的就是任務線程異常了,由於javadoc裏關於scheduleWithFixedDelay有這樣的警告:ide
當有異常產生時,後續定時任務就停掉了。可是代碼裏已經考慮了異常狀況,作了try/catch,而且沒有輸出錯誤日誌,只好修改代碼嘗試跟蹤下線程執行結果:測試
public class Application { private static ScheduledExecutorService timer = Executors.newScheduledThreadPool(2); public static void main(String[] args) throws ExecutionException, InterruptedException { ScheduledFuture<?> handle = timer.scheduleWithFixedDelay(() -> { try { //此處執行任務 } catch(Exception ex) { System.out.println(ex.getMessage()); } }, 0, 1, TimeUnit.SECONDS); handle.get(); //若是線程執行異常,此處會拋出 } }
用ScheduledFuture跟蹤,上面的測試程序固然不會報錯,但在實際環境裏打印了下面的異常:大數據
Exception in thread "pool-1-thread-1" java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError
這下搞清楚了,由於-Xmx參數不合理致使線程在處理某個大數據時拋出OOM,這個錯誤沒有被上面的try/catch捕獲,致使定時任務掛掉。所以使用ScheduledExecutorService時必定要注意考慮各類可能的異常,不過對於OOM,即便捕獲了能作的事也有限,但至少能夠保證定時任務能繼續,而且在日誌裏留下痕跡方便排查。線程