Handling Uncaught Exceptions in Java Thread Pools: execute vs submit
This article explains why uncaught exceptions cause thread pool threads to terminate, demonstrates experiments with execute and submit methods, and shows how catching exceptions or using Future.get() can preserve thread reuse in Java concurrency.
When a thread encounters an uncaught exception, it terminates and cannot be reused, causing the thread pool to create new threads.
The article demonstrates this with a simple experiment: a single‑thread pool repeatedly executes a task that divides by zero, causing an ArithmeticException. The output shows different thread names for each execution, indicating no reuse.
public class ThreadExecutor {
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build());
@Test
public void test() {
IntStream.rangeClosed(1,5).forEach(i -> {
try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
threadPoolExecutor.execute(() -> {
int j = 1/0;
});
});
}
}After catching the exception inside the task, the same thread is reused, confirming that handling the exception prevents thread termination.
public class ThreadExecutor {
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build());
@Test
public void test() {
IntStream.rangeClosed(1,5).forEach(i -> {
try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
threadPoolExecutor.execute(() -> {
try {
int j = 1/0;
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + " " + e.getMessage());
}
});
});
}
}Setting an uncaughtExceptionHandler via ThreadFactoryBuilder provides a fallback for uncaught exceptions, but using execute still terminates the thread.
new ThreadFactoryBuilder()
.setNameFormat("customThread %d")
.setUncaughtExceptionHandler((t, e) -> System.out.println(t.getName() + "发生异常" + e.getCause()))
.build();Switching to submit returns a Future. The exception is stored and re‑thrown when future.get() is called, allowing the thread to be reused.
Future<?> future = threadPoolExecutor.submit(() -> {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
int j = 1/0;
});
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}Internally, FutureTask captures the exception in its outcome and throws it from get() after the task completes.
Summary: manually catch exceptions in thread‑pool tasks, or use submit with Future to handle exceptions without losing thread reuse; setting an UncaughtExceptionHandler only provides a last‑resort log.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
