How to Handle Died Threads due to Uncaught Exceptions in Java

In concurrent applications a thread might fail and die due to uncaught runtime exceptions even without noticing since the application may continue to work. Losing one consumer thread from a thread pool can be tolerable, but losing a single dispatcher thread can degrade application workflow.

There are four alternative approaches in Java to get notified when a task fails due to an exception, so that you can log or take some recovery action.

Proactive Approach

In the solution below, run() method of a thread is structured with try-catch block and if a task throws an unchecked exception, it allows the thread to die. While handling the exception, the replacement of this worker thread with a new thread can be triggered.

Uncaught Exception Handler

Secondly, you can define an uncaught exception handler for the threads created by your custom thread factory, which is passed to the thread pool. When a thread exits due to an uncaught exception, the JVM reports this event to our UncaughtExceptionHandler, otherwise the default handler just prints the stack trace to standard error.

However, exceptions thrown from tasks make it to the uncaught exception handler only for tasks submitted with execute(); for tasks submitted with submit() to the executor service, any thrown exception is considered to be part of the task’s return status.

Thread Pool Executor Handler

If you want to be notified when a task fails due to an exception and take some task-specific recovery action, the afterExecute() method in ThreadPoolExecutor can be overridden. The solution below handles both cases you use submit() or execute() methods of the execution service.

Future Get Approach

Lastly, uncaught exceptions can then be handled by blocking on the get() function of the Future, which is returned after submitting the task. If a task submitted with submit() terminates with an exception, it is rethrown by Future.get() and wrapped in an ExecutionException.

You can use any one of these approaches or use together with proactive approach according to your requirements. My personal preference especially while using multiple thread pools is overriding afterExecute() method, and calling execute() again on detecting an uncatched exception to restart my always running tasks.

Please let me know if you have any comments or corrections.

Photograph by Joel Sartore