Handling exceptions from Java ExecutorService tasks
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
In Java, the ExecutorService framework provides a high-level concurrency API for managing multiple threads. It simplifies parallel execution by abstracting thread management details. However, a pivotal aspect of leveraging this framework is adeptly handling exceptions from tasks submitted to an ExecutorService.
Understanding Exception Handling in ExecutorService
When a task (or Callable) is executed in an ExecutorService, exceptions may occur. Unlike traditional single-threaded applications, handling these exceptions in a multithreaded environment requires special attention. Exceptions in a task do not surface on the main thread; instead, they remain encapsulated within the worker threads unless explicitly handled.
Key Concepts
- Runnable vs. Callable:
- Runnable does not return a result and cannot throw checked exceptions.
- Callable returns a result and can throw checked exceptions.
- Future: When a task is submitted, it returns a
Futureobject that acts as a handle for the task. It provides methods to check task completion (isDone()), wait for the task result (get()), and cancel the task (cancel()).
Handling Exceptions with Future
Using Future, you can catch exceptions by calling the get() method, which will throw an ExecutionException if the task aborted due to an exception.
Example
Exception Handling Strategies
- Immediate Attention with
get(): Usefuture.get()to retrieve the result, which blocks until the result is available or an exception is thrown. - Collecting Exceptions for Multiple Tasks: When handling multiple futures, you can aggregate exceptions and handle them collectively.
Example for Multiple Futures
Custom Uncaught Exception Handler
Use the ThreadPoolExecutor and override the afterExecute method to define a custom way of handling exceptions.
Example
Summary Table
| Key Consideration | Description |
| Runnable vs. Callable | Runnable cannot throw checked exceptions, whereas Callable can. |
| Future | Encapsulates task execution and result retrieval. |
get() Blocking | Retrieves the result, blocking until the task finishes, also throws an ExecutionException if an error occurs. |
| Exception Aggregation | Allows collection and handling of exceptions from multiple tasks. |
| Custom Handler | Implement a custom exception handler with ThreadPoolExecutor.afterExecute. |
Additional Considerations
- Handling Checked Exceptions: The
Callableinterface allows for throwing checked exceptions, which can be managed in thecall()method and propagated usingExecutionException. - Thread Interruption: Always handle the
InterruptedExceptionand restore the interrupt status when handling exceptions within the thread context.
Managing exceptions effectively in tasks submitted to an ExecutorService ensures robust multithreading applications capable of handling various runtime anomalies, thereby promoting application stability and reliability.
Related reading
- Handling interdependent and/or layered asynchronous calls
- Handling InterruptedException in Java
- Handling multiple returns asynchronously in node.js
- Handling Race Condition in distributed system
- Handling InterruptedException in Java
- Has been compiled by a more recent version of the Java Runtime class file version 57.0
- Handling exceptions in Kafka streams
- Handling Mongoose validation errors – where and how?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.