場景:
1、ExecutorService執行器中的 execute() 和 submit() 都是執行異步線程任務,但 execute() 沒有傳回值,而 submit() 允許有傳回值
2、CompletableFuture 的 runAsync() 沒有傳回值、supplyAsync() 有傳回值
一、ExecutorService 線程塊内異常:
1、execute() 執行過程中出現異常,會把異常資訊列印到控制台,程式終止執行
測試代碼:
System.out.println("測試execute()内部異常");
executorService.execute(() -> {
int i = 5 / 0;
System.out.println("executorService.submit()執行了");
});
輸出:
主線程執行開始
測試execute()内部異常
Exception in thread "pool-2-thread-5" java.lang.ArithmeticException: / by zero
at com.hkl.mpjoin.modules.testOne.TestOneController.lambda$testExecutors$1(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
線程池任務執行了
2、submit() 執行過程中出現異常,不會把異常資訊列印到控制台,程式會終止執行
測試代碼:
System.out.println("測試submit()内部異常");
executorService.submit(() -> {
int i = 5 / 0;
System.out.println("executorService.submit()執行了");
return "a";
});
輸出:
主線程執行開始
測試submit()内部異常
線程池任務執行了
小結:
1、在都不捕捉異常的情況下,execute() 會輸出異常資訊并終止程式,submit() 不會輸出異常資訊、但會終止程式
2、如果不需要傳回值的情況下,建議使用 execute() 執行異步線程,因為即使不捕捉也會輸出異常資訊到控制台或日志檔案,便于排查問題
3、如果需要傳回值,使用 submit() 方法塊需要用 try/catch 捕捉異常資訊手動列印 e.printStackTrace() 并輸出到日志檔案、并把捕捉的異常抛出去,否則無法檢視異常資訊,不便于排查問題
4、ScheduledExecutorService 日程線程池中 schedule()、execute() 和 submit() 都不會輸出異常資訊、但會終止程式,需要用 try/catch 捕捉異常列印出來 e.printStackTrace() (把捕捉的異常抛出去)
5、線程池、CompletableFuture 之類的都屬于異步線程,如果異步内出現異常隻打斷目前異步線程運作,不會打斷主線程運作
6、手動 try catch 列印、記錄日志、抛出異常等處理示例:
executorService.submit(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
log.error("executorService.submit 出現異常:" + e.getMessage());
throw e;
}
System.out.println("executorService.submit 執行了");
});
二、CompletableFuture 線程塊内異常:
1、CompletableFuture 的 runAsync()、supplyAsync() 等異步線程出現異常也不會列印輸出異常、但會終止目前線程運作
2、需要手動 try catch 列印、記錄日志、抛出異常等處理
CompletableFuture.runAsync(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
log.error("CompletableFuture.runAsync 出現異常:" + e.getMessage());
throw e;
}
System.out.println("CompletableFuture.runAsync 運作了");
});
CompletableFuture.supplyAsync(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
log.error("CompletableFuture.supplyAsync 出現異常:" + e.getMessage());
throw e;
}
System.out.println("CompletableFuture.supplyAsync 運作了");
return null;
});
3、如果異步内出現異常隻打斷目前異步線程運作,不會打斷主線程運作
4、包括延遲執行器的異步線程也是如此
CompletableFuture.runAsync(() -> {
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
log.error("CompletableFuture.runAsync 延遲2秒 出現異常:" + e.getMessage());
throw e;
}
System.out.println("CompletableFuture.runAsync 延遲2秒 運作了");
}, CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, executorService));