Exception Handling in Threads

By contract, Runnable#run method does not allow throwing exceptions. Possible ways of handling exceptions are try/catch blocks or setting exception handler on thread or thread group.

A thread or thread group allows us to set an exception handler on it, to receive exceptions occured during the execution of thread.

How it works

If you are clever enough, following flowchart says it all. Have a look!

thread running
<span>thread running</span>
start
start
exception occurs
exception occurs
UncaughtExceptionHandler 
set on thread?
[Not supported by viewer]
call uncaughtException
(Thread, Throwable)
[Not supported by viewer]
end
end
Yes
Yes
No
No
call uncaughtException
(Thread, Throwable) 
in thread group
[Not supported by viewer]
is there any parent
thread group?
[Not supported by viewer]
Yes
Yes
is 
DefaultUncaughtExceptionHandler 
set on thread?
[Not supported by viewer]
No
No
call it with exception and thread as arg
call it with exception and thread as arg
Yes
Yes
end
end
print stacktrace to
error stream and
terminate thread
[Not supported by viewer]
No
No

Example

Create a general ExceptionalThread class which inherits from Thread and generates ArrayIndexOutOfBoundsException when run. See the code below

class ExceptionalThread extends Thread {

    ExceptionalThread(String name) {
        super(name);
    }


    ExceptionalThread(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        super.run();
        // generates ArrayIndexOutOfBoundsException
        final int unused = (new int[2])[3];
        System.out.println(unused);
    } // run

} // ExceptionalThread

Inherit 3 more classes from this class with names DefaultHandler, ThreadGroupHandler, ThreadSpecificHandler.

  • ThreadSpecificHandler thread sets its UncaughtExceptionHandler and exception will be caught in this handler.

  • ThreadGroupHandler thread calls uncaughtException method of its thread group.

  • DefaultHandler thread calls DefaultUncaughtExceptionHandler of the thread.

Code for all three classes is given below

ThreadSpecificHandler

class ThreadSpecificHandler extends ExceptionalThread {

    ThreadSpecificHandler() {
        super("ThreadSpecificHandler");
    }

} // ThreadSpecificHandler

ThreadGroupHandler

class ThreadGroupHandler extends ExceptionalThread {

    ThreadGroupHandler(ThreadGroup tg) {
        super(tg, "ThreadGroupHandler");
    }
} // ThreadGroupHandler

DefaultHandler

class DefaultHandler extends ExceptionalThread {

    DefaultHandler() {
        super("DefaultHandler");
    }

    @Override
    public void run() {
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> App.exceptionHandler(t, e, "Thread#UncaughtExceptionHandler (Default)"));
        super.run();
    }
} // DefaultHandler

App

public class App {

    static void exceptionHandler(Thread t, Throwable exp, String method){
        System.out.println(String.format("Method: %s - ThreadName: %s - Exception: %s", method, t.getName(), exp.getClass().getSimpleName()));
    } // exceptionHandler

    public static void main(String[] args) throws InterruptedException {

        Thread threadWithHandler = new ThreadSpecificHandler();
        threadWithHandler.setUncaughtExceptionHandler((t, e) -> exceptionHandler(t, e, "Thread#UncaughtExceptionHandler"));
        threadWithHandler.start();

        /*  ------------------- THREAD GROUP ---------------*/

        ThreadGroup group = new ThreadGroup("exp"){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
               exceptionHandler(t, e, "ThreadGroup#uncaughtException");
            }
        };
		/*  ------------------- THREAD GROUP ---------------*/

        Thread threadWithGroupHandler = new ThreadGroupHandler(group);
        threadWithGroupHandler.start();


        Thread threadWithDefaultHandler = new DefaultHandler();
        threadWithDefaultHandler.start();


        // wait for threads
        threadWithDefaultHandler.join();
        threadWithGroupHandler.join();
        threadWithHandler.join();

    } // main

} // App

Output

Method: ThreadGroup#uncaughtException - ThreadName: ThreadGroupHandler - Exception: ArrayIndexOutOfBoundsException
Method: Thread#UncaughtExceptionHandler (Default) - ThreadName: DefaultHandler - Exception: ArrayIndexOutOfBoundsException
Method: Thread#UncaughtExceptionHandler - ThreadName: ThreadSpecificHandler - Exception: ArrayIndexOutOfBoundsException

top