天天看點

線程池ExecutorService、CompletableFuture異步方法解析

場景:

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));