使用ScheduledExecutorService執行定時任務時一定要注意各種異常捕獲
阿新 • • 發佈:2020-10-28
近期一個專案有個定時任務阻塞住了,從日誌裡看沒有任何異常產生,但就是定時不再執行了,程序還在,jstack看了下執行緒處於WAIT狀態,但就是不再定時觸發。於是拿程式碼分析了一下,程式碼原理很簡單,拿ScheduledExecutorService.scheduleWithFixedDelay設定的定時任務,簡化後類似這樣:
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有這樣的警告:
當有異常產生時,後續定時任務就停掉了。但是程式碼裡已經考慮了異常情況,做了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,即使捕獲了能做的事也有限,但至少可以保證定時任務能繼續,並且在日誌裡留下痕跡方便排查。