Wednesday, December 12, 2012

Handling Thread Exceptions

I've been a witness when interviewer asked candidate a question:
- "Is it any way to handle exception thrown within particular thread from client thread?"
Imagine the situation when you need to know if any exception was thrown in thread you scheduled and run. Surround Thread#start() with try-catch clause doesn't make any sense, because it's asynchronous call of Runnable.

I can suggest at least 2 ways to do it

1. Old school approach.
One can register Exception handler before starting the thread:

Thread thread = new Thread(new Runnable() {   
 public void run() {
  throw new RuntimeException("simulate failure for oldschool approach");    
  }
 });
thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
 public void uncaughtException(Thread t, Throwable e) {
  System.out.println("I'm in thread " + Thread.currentThread().getName());
  e.printStackTrace();
 }
});  
thread.start();

The output is following:

I'm in thread Thread-0 
java.lang.RuntimeException: simulate failure for oldschool approach 
at org.sdo.concurrency.interview.ExceptionWithinThreadTask$1.run(ExceptionWithinThreadTask.java:26) 
at java.lang.Thread.run(Thread.java:722)


UncaughtExceptionHandler#uncaughtException was executed in newly created thread. To make it visible from client thread synchronization should be used (Thread#join for instance)

2. Futures approach

System.out.println("Simulating using future");
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
Future future = newSingleThreadExecutor.submit(new Callable
<String>() {
 public String call() throws Exception {
  throw new RuntimeException("simulate fail");
 }
 });
try {
 future.get();
}
catch (InterruptedException e) {
 e.printStackTrace();
}
catch (ExecutionException e) {
 System.out.println("I'm in thread " + Thread.currentThread().getName());
 e.printStackTrace();
}
newSingleThreadExecutor.shutdown();

The output is:

I'm in thread main
java.util.concurrent.ExecutionException: java.lang.RuntimeException: simulate fail
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at org.sdo.concurrency.interview.ExceptionWithinThreadTask.simulateUsingFuture(ExceptionWithinThreadTask.java:52)
at org.sdo.concurrency.interview.ExceptionWithinThreadTask.main(ExceptionWithinThreadTask.java:18)
Caused by: java.lang.RuntimeException: simulate fail
at org.sdo.concurrency.interview.ExceptionWithinThreadTask$3.call(ExceptionWithinThreadTask.java:48)
at org.sdo.concurrency.interview.ExceptionWithinThreadTask$3.call(ExceptionWithinThreadTask.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)

Pay attention that exception was handled in client thread.


Conclusion

The second approach is more natural. We can surround Future#get() with try-catch clause and handle inner-thread exception from client threads. From the other side using oldschool approach leads to additional effort to make throwable visible from client thread.