Wednesday, October 8, 2014

Java Thread I: Thread Exception Handling with Example

This post will explain simple example of exception handling in java threads.

Have you ever wondered what happens when multiple threads are accessing shared data and one of them throws exception and you don't even know which thread did it and what to do next !!

What happens if one of the Strings is null and we get a NullPointerException? What happens if we run out of memory and get an OutOfMemoryError? What happens if we get an OOME and at the same time the connection does not work anymore? Then the finally would cause an exception, which would mask the OOME, and make it disappear. There are lots of possibilities, and if we try to cater for all eventualities (excuse the pun) then we will go crazy trying and our code will look rather complicated.

See below piece of code and try to see what could be the output here,

public static void main(String[] args) {
Thread t1 = new Thread(new MyThreadExceptionHandling());
Thread t2 = new Thread(new MyThreadExceptionHandling());
t1.start();
t2.start();
new RuntimeException(); 
}

@Override
public void run(){
System.out.println("Creating thread");
int i = 1/0;
}

Output:
Creating thread
Creating thread
Exception in thread "Thread-1" java.lang.ArithmeticException: / by zero
at com.practice.java.threads.ThreadExceptionHandling.run(ThreadExceptionHandling.java:36)
at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at com.practice.java.threads.ThreadExceptionHandling.run(ThreadExceptionHandling.java:36)
at java.lang.Thread.run(Thread.java:619)

So in the real world, how are exceptions handled? Frequently, exceptions are stubbed out and ignored, because the writer of the code did not know how to handle the error (and was going to go back and fix it, one day, but the project manager was breathing down his neck and the release had to go out that afternoon). This is bad, since you then do not know that something has gone awry. On the other hand, if the exception bubbles up the call stack, it may kill the thread, and you may never know that there was an exception.

I have witnessed production code do things like this (I kid you not):


    try {

      // do something
    } catch(Exception ex) {
      // log to some obscure log file, maybe
      return "";
    }
  
The effect was that the webpage showed empty strings as values when something went wrong with the code.

My approach to exceptions is to have a central mechanism that deals with any exceptions that I am not 100% sure of how to handle. Whenever something goes wrong, this central place is notified. However, what happens when you are using someone else's code and their threads die without warning?


Java came up with uncaught exception handlers (Thread.UncaughtExceptionHandler) from jdk 1.5 onwards.


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:

  • You can set it for a specific Thread  (custom handler for thread t)
  • You can set it for a ThreadGroup (which means for all Threads in that group)
  • You can set it VM wide for all Threads regardless (default handler)
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):

private class MyThreadExceptionHandler implements UncaughtExceptionHandler{

@Override

public void uncaughtException(Thread t, Throwable e) {
                   StringWriter sw = new StringWriter();
                   e.printStackTrace(new PrintWriter(sw));
                   String stacktrace = sw.toString();

System.out.println("ERROR! An exception occurred in " + t.getName() + ". Cause: " + e.getMessage());

                        System.out.println(stacktrace);
sendMail(stacktrace); //send mail to interested parties or stack holders.

}
}

Now, change the main() method as below:

public static void main(String[] args) {

Thread t1 = new Thread(new ThreadExceptionHandling());
Thread t2 = new Thread(new ThreadExceptionHandling());
Thread.UncaughtExceptionHandler exception = new ThreadExceptionHandling(). new MyThreadExceptionHandler();
      
// Add the handler to the thread object
Thread.setDefaultUncaughtExceptionHandler(exception);
// t1.setUncaughtExceptionHandler(custoException1);
// t2.setUncaughtExceptionHandler(customException2);
t1.start();
t2.start();
new RuntimeException(); //main class is also a thread and it doesnt have any exception handler.
}

Now, try to run the program and check the output. It makes life a lot easier since you don't have to break your head thinking of all possible failure scenarios upfront and make your code complex in handling them..................................

Working code for above example can be found here.

No comments:

Post a Comment