Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Uncaught Java Thread Exceptions

February 11, 2013

I recently encountered an issue where a worker thread was terminating quietly in some code I hadn't written, and couldn't really change. In this case, the thread was terminating due to an exception it had encountered. I wanted to robustly know when this thread terminated so that I could log it, and then restart it to continue the processing it was doing. I'll explain how I resolved this, but first, let's examine some code that illustrates the problem:

public class MyApp {
    static int count = 0;
    
    public class MyWorker extends Thread {
        public void run() {
            while ( true ) {
                try {
                    // Do this work every second forever unless interuppted
                    doWork();
                    Thread.sleep(1000);
                }
                catch ( InterruptedException e ) { 
                    return;
                }
            }
        }
        
        private void doWork() {
            // Simulate work that sometimes results in NullPointerException
            StringBuffer sb = new StringBuffer("My Work counter: ");
            if ( count++ >= 5 ) {
                sb = null; //oops!
                count = 0;
            }
            sb.append(count);
            System.out.println(sb.toString());
        }
    }
    
    public MyApp() {
        MyWorker worker = new MyWorker();
        worker.start();
    }
    
    public static void main(String[] args) {
        MyApp te = new MyApp();
    }
}

To summarize, we have a main class, MyApp, with a nested class, MyWorker, which extends Thread. In the run method, a call is made to the doWork method every second forever unless the thread is interrupted. However, the code has a bug, simulated by the creating of a NullPointerException, which is never handled, thus terminating the Thread. The issue is that the application code never really knows that it happens and the periodic processing that the Thread was doing has now ceased.

Thread.UncaughtExceptionHandler

According to the Java API documentation, when a thread is about to terminate due to an uncaught exception, the Java Virtual Machine will query the thread for its UncaughtExceptionHandler using Thread.getUncaughtExceptionHandler() and will invoke the handler's uncaughtException method, passing the thread and the exception as arguments. If a thread has not had its UncaughtExceptionHandler explicitly set, then its ThreadGroup object acts as its UncaughtExceptionHandler. If the ThreadGroup object has no special requirements for dealing with the exception, it can forward the invocation to the default uncaught exception handler.

Java allows you to install a default exception handler for threads, for just this very reason. You have three options in doing so:

  1. You can set it for a specific Thread
  2. You can set it for a ThreadGroup (which means for all Threads in that group)
  3. You can set it VM wide for all Threads regardless

In our example, where the Exception occurs in code we cannot change, we'll need to go with option 3. Doing so is very easy, and allows us to log the Exception that has occurred, get the full stack trace, and then restart the worker Thread since it will have terminated by the time the exception handler is notified. Here is the code modified to install a default exception handler (only the modified code is shown):

public class MyApp {

    ...

    public MyApp() {
        Thread.setDefaultUncaughtExceptionHandler(
                new Thread.UncaughtExceptionHandler() {
                    @Override public void uncaughtException(Thread t, Throwable e) {
                        System.out.println(t.getName()+": "+e);
                        MyWorker worker = new MyWorker();
                        worker.start();
                    }
                });
        
        MyWorker worker = new MyWorker();
        worker.start();
    }
    
    public static void main(String[] args) {
        MyApp te = new MyApp();
    }
}

With this change when the unhanded NullPointerException occurs, the default UncaughtExceptionHandler we installed is invoked, which logs the thread name and the exception, and then restarts the worker thread to continue its processing. Although the original problem of the thread terminating still occurs, at least we now have insight as to when it happens, and can take action. In this example, we simply restart the worker thread to ensure the processing still occurs.

Executing this code now shows the following:

My Work counter: 1
My Work counter: 2
My Work counter: 3
My Work counter: 4
My Work counter: 5
Thread-0: java.lang.NullPointerException
My Work counter: 1
My Work counter: 2
My Work counter: 3
My Work counter: 4
My Work counter: 5
Thread-1: java.lang.NullPointerException
My Work counter: 1
My Work counter: 2
My Work counter: 3
My Work counter: 4
My Work counter: 5
Thread-2: java.lang.NullPointerException
My Work counter: 1
My Work counter: 2
...

This output continues indefinitely, as it was intended.

Alternatively, we can install the uncaught exception handler for just a specific thread. Using our example, since MyWorker extends Thread, we can call its setDefaultUncaughtExceptionHandler() method and install the same handler code, as shown here:

        MyWorker worker = new MyWorker();
        worker.setDefaultUncaughtExceptionHandler(
                new Thread.UncaughtExceptionHandler() {
                    @Override public void uncaughtException(Thread t, Throwable e) {
                        System.out.println(t.getName()+": "+e);
                        MyWorker worker = new MyWorker();
                        worker.start();
                    }
                });
        worker.start();

Perhaps you'll find this useful in your own code, as I have, to help ensure the robust handling of exceptions, especially when it comes to concurrent processing in worker threads.

Happy coding!
-EJB

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video