How Java Thread Pools Handle Exceptions: execute vs submit
This article investigates how Java's java.util.concurrent.ExecutorService thread pools react to exceptions when tasks are submitted via execute versus submit, providing source‑code experiments, observed outcomes, and a detailed analysis of the underlying JDK implementation.
The article examines the behavior of Java java.util.concurrent.ExecutorService thread pools when a task throws an exception, comparing the execute and submit submission methods.
1. execute() test
Test code:
public class ThreadPoolExecutorDeadTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = buildThreadPoolExecutor();
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute-exception"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
Thread.sleep(5000);
System.out.println("再次执行任务=======================");
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
}
public static ExecutorService buildThreadPoolExecutor() {
return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
private static void exeTask(String name) {
String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]";
if ("execute-exception".equals(name)) {
throw new RuntimeException(printStr + ", 我抛异常了");
} else {
System.out.println(printStr);
}
}
}Result: the thread that throws an exception is removed from the pool and a new thread is created for subsequent tasks.
Conclusion for execute() : an uncaught exception causes the offending thread to be terminated and replaced by a fresh thread.
2. submit() test
Test code:
public class ThreadPoolExecutorDeadTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = buildThreadPoolExecutor();
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute-exception"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
Thread.sleep(5000);
System.out.println("再次执行任务=======================");
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
}
public static ExecutorService buildThreadPoolExecutor() {
return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
private static void exeTask(String name) {
String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]";
if ("execute-exception".equals(name)) {
throw new RuntimeException(printStr + ", 我抛异常了");
} else {
System.out.println(printStr);
}
}
}Result: the exception does not propagate to the caller, no stack trace is printed, and the thread remains in the pool; no new thread is created.
Conclusion for submit() : the exception is captured inside the returned Future ; the original worker thread stays alive and is reused.
3. Source‑code analysis
Investigation of java.util.concurrent.AbstractExecutorService#submit shows it wraps the task in a FutureTask , which handles exceptions internally. The execute path directly enqueues the runnable, so an uncaught exception terminates the worker thread, triggering ThreadPoolExecutor#processWorkerExit to replace it.
Key observations:
execute(): exception → thread removal → new thread creation.
submit(): exception → stored in Future → no thread removal.
Both methods do not affect other threads in the pool.
Summary : When a task submitted via execute throws an uncaught exception, the thread pool discards the faulty thread and spawns a replacement; when the same task is submitted via submit , the exception is captured by the Future , the thread stays alive, and no new thread is created.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
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.