1. 程式人生 > 實用技巧 >使用ScheduledExecutorService執行定時任務時一定要注意各種異常捕獲

使用ScheduledExecutorService執行定時任務時一定要注意各種異常捕獲

近期一個專案有個定時任務阻塞住了,從日誌裡看沒有任何異常產生,但就是定時不再執行了,程序還在,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,即使捕獲了能做的事也有限,但至少可以保證定時任務能繼續,並且在日誌裡留下痕跡方便排查。