Java面試題:執行緒池內“鬧情緒”的執行緒,怎麼辦?

猫鱼吐泡泡發表於2024-05-12

在Java中,執行緒池中工作執行緒出現異常的時候,預設會把異常往外拋,同時這個工作執行緒會因為異常而銷燬,我們需要自己去處理對應的異常,異常處理的方法有幾種:

  • 在傳遞的任務中去處理異常,對於每個提交到執行緒池中的執行的任務,可以提前透過異常進行捕獲,這樣即便出現了異常,也不會影響執行緒池中的工作執行緒

  • 使用Future來捕獲異常結果,線上程池中提供了一個submit(Callable<T>)方法,這個方法會返回一個Future,可以透過呼叫Future.get()方法,來獲取任務的執行結果,如果任務執行過程中出現了異常,也會丟擲一個ExecutionException,其中就包含了任務執行過程中出現的異常

  • 我們還可以自定義一個ThreadFactory,設定一個UncaughtExceptionHandler,我們可以透過實現ThreadFactory的介面來自定義建立執行緒的方式,然後為每個新建立的執行緒設定一個UncaughtExceptionHandler,這個處理器會線上程由於未捕獲異常而即將終止的時候被呼叫

下面是三段程式碼示例:

捕獲執行緒執行異常

ExecutorService executorService = Executors.newFixedThreadPool(5);

executorService.execute(() -> {
    try {
        // 執行任務
    } catch (Exception e) {
        // 記錄日誌
        logger.error("An exception occurred: ", e);
    }
});

在執行任務的過程中,如果出現異常,就會被try-catch語句捕獲,並將異常資訊記錄到日誌中。

使用Future來捕獲異常結果

ExecutorService executorService = Executors.newFixedThreadPool(5);

List<Future<?>> futures = new ArrayList<>();

// 提交任務到執行緒池
for (int i = 0; i < 10; i++) {
    Future<?> future = executorService.submit(() -> {
        // 執行任務
    });
    futures.add(future);
}

// 獲取任務結果
for (Future<?> future : futures) {
    try {
        future.get();
    } catch (InterruptedException | ExecutionException e) {
        // 處理異常
        logger.error("An exception occurred: ", e);
    }
}

在這個例子中,我們將多個任務提交到執行緒池,並將每個任務的Future物件儲存在futures列表中。接著,我們遍歷futures列表,並呼叫每個Future物件的get()方法來獲取任務的執行結果。如果任務執行過程中出現了異常,則會丟擲ExecutionException異常,我們在catch塊中捕獲該異常並進行相應的處理。

實現UncaughtExceptionHandler介面

public class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 記錄日誌或者進行其它處理
        logger.error("Thread " + t.getName() + " encountered an exception: ", e);
    }
}

ExecutorService executorService = Executors.newFixedThreadPool(5);

// 設定UncaughtExceptionHandler
((ThreadPoolExecutor) executorService).setThreadFactory(r -> {
    Thread thread = new Thread(r);
    thread.setUncaughtExceptionHandler(new CustomExceptionHandler());
    return thread;
});

executorService.execute(() -> {
    // 執行任務
});

這種方式需要自定義一個實現了UncaughtExceptionHandler介面的異常處理器,當執行緒出現異常時,異常處理器會被呼叫,我們可以在其中記錄日誌或者進行其它處理。接著,我們需要將異常處理器設定到執行緒池的執行緒工廠中。當執行緒池內的執行緒出現異常時,異常處理器就會被呼叫,我們可以在其中處理異常。

  

往期面試題:

Java面試題:@PostConstruct、init-method和afterPropertiesSet執行順序?

Java面試題:SimpleDateFormat是執行緒安全的嗎?使用時應該注意什麼?

Java面試題:細數ThreadLocal大坑,記憶體洩露本可避免

Java面試題:請談談對ThreadLocal的理解?

Java面試題:為什麼HashMap不建議使用物件作為Key?

Java面試題:你知道Spring的IOC嗎?那麼,它為什麼這麼重要呢?

Java面試題:Spring Bean執行緒安全?別擔心,只要你不寫併發程式碼就好了!

相關文章