multithreading
exception-handling
java
programming
concurrency

Catch a thread's exception in the caller thread?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

In concurrent programming, one of the challenges developers face is managing exceptions that occur in one thread and need to be caught and handled in another. This scenario often arises when the main thread initiates a task in a separate thread and must handle any exceptions thrown by that task. In Java, this involves certain techniques and patterns to pass exceptions from child threads back to the main thread.

Exception Handling in Threads

When an exception occurs in a thread, it typically does not affect the other threads. This isolation is useful for robustness but poses challenges when you want the main thread or another thread to react to exceptions thrown in a worker thread.

Key Concepts

Before diving into solutions, it’s essential to understand these concepts:

  • Thread Isolation: Unlike exceptions in a single-threaded application, unhandled exceptions in a specific thread will not terminate or affect other threads directly.
  • Callable and Future: Java provides the Callable interface and the Future object to handle return values and exceptions from threads.
  • Thread UncaughtExceptionHandler: For uncaught exceptions, Java's Thread class allows an UncaughtExceptionHandler to capture them. This handler is executed in the same thread where the exception occurred.

Techniques for Catching Exceptions

Using Callable and Future

The Callable interface in Java is similar to Runnable, but it can return a result and throw a checked exception. By submitting a Callable to an ExecutorService, you receive a Future object. The get() method of Future blocks until the task completes and will throw an ExecutionException if the callable threw an exception.

java
1import java.util.concurrent.Callable;
2import java.util.concurrent.ExecutionException;
3import java.util.concurrent.ExecutorService;
4import java.util.concurrent.Executors;
5import java.util.concurrent.Future;
6
7public class ThreadExceptionExample {
8    public static void main(String[] args) {
9        ExecutorService executor = Executors.newFixedThreadPool(1);
10
11        Callable<Void> task = () -> {
12            throw new Exception("Exception in Thread");
13        };
14
15        Future<Void> future = executor.submit(task);
16
17        try {
18            future.get();
19        } catch (InterruptedException | ExecutionException e) {
20            System.out.println("Caught exception: " + e.getCause());
21        } finally {
22            executor.shutdown();
23        }
24    }
25}

Using UncaughtExceptionHandler

The UncaughtExceptionHandler is a mechanism to catch unchecked exceptions in a thread. This approach works for runtime exceptions and errors but not for checked exceptions.

java
1public class UncaughtExceptionExample {
2    public static void main(String[] args) {
3        Thread thread = new Thread(() -> {
4            throw new RuntimeException("Unchecked Exception");
5        });
6
7        thread.setUncaughtExceptionHandler((t, e) -> {
8            System.out.println("Caught exception from thread: " + e.getMessage());
9        });
10
11        thread.start();
12    }
13}

Summary Table

MethodHandles CheckedHandles UncheckedMain Thread CatchBlocking Call Required
Callable & FutureYesYesYesYes, on Future.get() method (Exception wrapped in ExecutionException)
UncaughtExceptionHandlerNoYesNoNo, exception caught directly in the worker thread

Additional Details

Limitations and Considerations

  1. Latency: Using Future.get() is blocking and might cause latency if not managed properly. It should be used where the main thread can afford to wait for the task completion.
  2. Exception Wrapping: In the case of Callable and Future, exceptions are wrapped in ExecutionException, needing additional unwrapping to get the original cause.
  3. Practical Use-Cases: These mechanisms can be handy in server environments, GUI applications, or any context where long-running or non-blocking operations are executed on separate threads or thread pools.

Alternative Approaches

The above techniques highlight standard ways to manage thread exceptions in Java. However, several modern programming approaches, such as reactive programming with libraries like RxJava, offer abstractions to handle asynchronous flows and exceptions in a more functional style, improving readability and reducing boilerplate code.


Understanding how to manage exceptions across threads effectively is critical in building robust, concurrent software. Leveraging Java's concurrent utilities correctly ensures that exceptions are handled gracefully, maintaining application stability and responsiveness.


Course illustration
Course illustration

All Rights Reserved.