Handling Exceptions in Java ThreadPool: submit vs execute and Custom Solutions
This article explains why exceptions thrown by tasks submitted to a Java thread pool behave differently with submit and execute, demonstrates how to capture those exceptions using Future.get, try‑catch, Thread.setDefaultUncaughtExceptionHandler, and by overriding afterExecute, and provides complete code examples for each approach.
In Java development, thread pools are frequently used, but when a task throws an exception the handling differs between submit and execute . The article first shows a simple example where submit silently discards the exception while execute prints it.
It then explains that the exception from a submit call can be retrieved via the returned Future using future.get() , which propagates the exception as an ExecutionException .
Solution 1 – try‑catch inside the task demonstrates wrapping the task logic with a try‑catch block so the exception is printed directly.
public class ThreadPoolException {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(new task());
executorService.execute(new task());
}
}
class task implements Runnable {
@Override
public void run() {
try {
System.out.println("进入了task方法!!!");
int i = 1/0;
} catch (Exception e) {
System.out.println("使用了try‑catch捕获异常" + e);
}
}
}Solution 2 – Thread.setDefaultUncaughtExceptionHandler creates a custom thread factory that assigns an UncaughtExceptionHandler to each thread, allowing uncaught exceptions from execute to be logged.
ThreadFactory factory = (Runnable r) -> {
Thread t = new Thread(r);
t.setDefaultUncaughtExceptionHandler((thread, e) -> {
System.out.println("线程工厂设置的exceptionHandler" + e.getMessage());
});
return t;
};
ExecutorService executorService = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10), factory);
executorService.submit(new task());
Thread.sleep(1000);
executorService.execute(new task());Solution 3 – overriding afterExecute extends ThreadPoolExecutor and overrides afterExecute to process exceptions for both execute and submit . For submit , the runnable is a FutureTask , so the overridden method checks the type and calls future.get() to retrieve the exception.
ExecutorService executorService = new ThreadPoolExecutor(2, 3, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10)) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
if (t != null) {
System.out.println("afterExecute捕获execute异常: " + t.getMessage());
}
if (r instanceof FutureTask) {
try {
((Future
) r).get();
} catch (Exception e) {
System.out.println("afterExecute捕获submit异常: " + e);
}
}
}
};
executorService.execute(new task());
executorService.submit(new task());The article concludes that for tasks where exceptions must not be lost, using execute with proper try‑catch or a custom afterExecute implementation is safer, while submit requires retrieving the exception via the returned Future .
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.