Handling Exceptions in Java ThreadPool: submit vs execute and Custom Solutions

This article explains why tasks submitted to a Java ThreadPool via submit do not print exceptions, how to retrieve them using Future.get(), and presents three practical solutions—including try‑catch, Thread.setDefaultUncaughtExceptionHandler, and overriding afterExecute—to reliably capture and process thread pool exceptions.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Handling Exceptions in Java ThreadPool: submit vs execute and Custom Solutions

In Java development, thread pools are frequently used, but when a task throws an exception, the handling differs between submit and execute. This article demonstrates the behavior, shows how to obtain exception information, and provides three concrete solutions.

Code example of a task that throws an exception:

public class ThreadPoolException {
    public static void main(String[] args) {
        // Create a thread pool with a single thread
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        // submit does not print the exception, other threads continue
        executorService.submit(new task());
        // execute throws the exception, other threads continue with new tasks
        executorService.execute(new task());
    }
}

// Task class
class task implements Runnable {
    @Override
    public void run() {
        System.out.println("Entering task method!!!");
        int i = 1 / 0; // This will cause ArithmeticException
    }
}

Running the above code shows that execute prints the exception while submit silently discards it. In production, silently lost exceptions are unacceptable.

To retrieve the exception from a submit call, you must use the returned Future:

Future<?> submit = executorService.submit(new task());
submit.get(); // This will throw ExecutionException containing the original exception

Solution 1: Use try‑catch inside the task

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("Entering task method!!!");
            int i = 1 / 0;
        } catch (Exception e) {
            System.out.println("Caught exception with try‑catch: " + e);
        }
    }
}

Both submit and execute now capture the exception via the explicit try‑catch block.

Solution 2: Use Thread.setDefaultUncaughtExceptionHandler

Instead of adding try‑catch to every task, you can set a global uncaught exception handler in a custom thread factory:

public class ThreadPoolException {
    public static void main(String[] args) throws InterruptedException {
        ThreadFactory factory = (Runnable r) -> {
            Thread t = new Thread(r);
            t.setDefaultUncaughtExceptionHandler((thread, e) -> {
                System.out.println("Thread factory handler: " + e.getMessage());
            });
            return t;
        };
        ExecutorService executorService = new ThreadPoolExecutor(
                1, 1, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(10), factory);
        executorService.submit(new task()); // No output from handler
        Thread.sleep(1000);
        System.out.println("=== after 1s, executing ===");
        executorService.execute(new task()); // Handler prints exception
    }
}

class task implements Runnable {
    @Override
    public void run() {
        System.out.println("Entering task method!!!");
        int i = 1 / 0;
    }
}

The handler catches exceptions from execute but not from submit, because submit wraps the task in a FutureTask that swallows the exception.

Solution 3: Override afterExecute in a custom ThreadPoolExecutor

By overriding afterExecute, you can process exceptions for both execute and submit submissions:

public class ThreadPoolException3 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = new ThreadPoolExecutor(
                2, 3, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(10)) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                if (t != null) {
                    System.out.println("afterExecute caught exception from execute: " + t.getMessage());
                }
                if (r instanceof FutureTask) {
                    try {
                        ((Future<?>) r).get();
                    } catch (Exception e) {
                        System.out.println("afterExecute caught exception from submit: " + e);
                    }
                }
            }
        };
        executorService.execute(new task());
        executorService.submit(new task());
    }
}

class task implements Runnable {
    @Override
    public void run() {
        System.out.println("Entering task method!!!");
        int i = 1 / 0;
    }
}

This approach prints exception information for both submission methods, providing a unified handling strategy.

In summary, execute propagates exceptions directly, while submit hides them inside a Future. Use Future.get(), a global uncaught exception handler, or override afterExecute to ensure exceptions are not lost.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyThreadPoolExecutorServiceexceptionhandling
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.