I'm working on a software development framework for parallel computing JavaSeis.org. I need a robust mechanism for reporting thread exceptions. During development, knowing where exceptions came from has high value, so I would like to err on the side of over-reporting. I would also like to be able to handle Junit4 testing in threads as well. Is the approach below reasonable or is there a better way ?
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestThreadFailure {
public static void main(String[] args) {
int size = 1;
ExecutorService exec = Executors.newFixedThreadPool(size);
ThreadFailTask worker = new ThreadFailTask();
Future<Integer> result = exec.submit(worker);
try {
Integer value = result.get();
System.out.println("Result: " + value);
} catch (Throwable t) {
System.out.println("Caught failure: " + t.toString());
exec.shutdownNow();
System.out.println("Stack Trace:");
t.printStackTrace();
return;
}
throw new RuntimeException("Did not catch failure !!");
}
public static class ThreadFailTask implements Callable<Integer> {
@Override
public Integer call() {
int nbuf = 65536;
double[][] buf = new double[nbuf][nbuf];
return new Integer((int) buf[0][0]);
}
}
}
If you want to process exceptions thrown by the task, then it is generally better to use Callable rather than Runnable . If Callable. call() throws an exception, this will be wrapped in an ExecutionException and thrown by Future. get() .
Consider adding a static event in your ScheduledThreadPoolExecutor class that any of your tasks can call if an exception is thrown. That way, you can leverage that event to capture and handle the exceptions that occur within your threads.
Once the thread has delegated the task to the ExecutorService , the thread continues its own execution independent of the execution of that task. The ExecutorService then executes the task concurrently, independently of the thread that submitted the task.
As an example, if the thread throws an exception and pool class does not catch this exception, then the thread will simply exit, reducing the size of the thread pool by one. If this repeats many times, then the pool would eventually become empty and no threads would be available to execute other requests.
Consider calling execute()
instead of submit()
on the ExecutorService
. A Thread
invoked with execute()
will invoke the Thread.UncaughtExceptionHandler
when it fails.
Simply make a ThreadFactory
that installs a Thread.UncaughtExceptionHandler
on all Threads
and then invoke your work with execute()
on the ExecutorService
instead of submit()
.
Have a look at this related stack overflow question.
I don't believe there is a standard 'hook' to get to these exceptions when using submit()
. However, if you need to support submit()
(which sounds reasonable, given that you use a Callable
), you can always wrap the Callables and Runnables :
ExecutorService executor = new ThreadPoolExecutor(1, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()) {
@Override
public <T> Future<T> submit(final Callable<T> task) {
Callable<T> wrappedTask = new Callable<T>() {
@Override
public T call() throws Exception {
try {
return task.call();
}
catch (Exception e) {
System.out.println("Oh boy, something broke!");
e.printStackTrace();
throw e;
}
}
};
return super.submit(wrappedTask);
}
};
Of course, this method only works if you're the one building the ExecutorService
in the first place. Furthermore, remember to override all three submit()
variants.
As explained in this thread What is the difference between submit and execute method with ThreadPoolExecutor, using execute will only work if you implement Runnable and not Callable as execute cannot return a Future.
I think in your scenario you should build the future object so that it can accommodate the exception stuff also. So in case of exception you build the error message object.
My original question asked how to implement "robust" thread exception handling with Java ExecutorService. Thanks to Angelo and Greg for pointers on how exception handling works with ExecutorService.submit() and Future.get(). My revised code fragment is shown below. The key point I learned here is that Future.get() catches all exceptions. If the the thread was interrupted or cancelled, you get the appropriate exception, otherwise, the exception is wrapped and re-thrown as an ExecutionException.
import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestThreadFailure { public static void main(String[] args) { int size = 1; ExecutorService exec = Executors.newFixedThreadPool(size); ThreadFailTask worker = new ThreadFailTask(); Future result = exec.submit(worker); try { Integer value = result.get(); System.out.println("Result: " + value); } catch (ExecutionException ex) { System.out.println("Caught failure: " + ex.toString()); exec.shutdownNow(); return; } catch (InterruptedException iex) { System.out.println("Thread interrupted: " + iex.toString()); } catch (CancellationException cex) { System.out.println("Thread cancelled: " + cex.toString()); } exec.shutdownNow(); throw new RuntimeException("Did not catch failure !!"); } public static class ThreadFailTask implements Callable { @Override public Integer call() { int nbuf = 65536; double[][] buf = new double[nbuf][nbuf]; return new Integer((int) buf[0][0]); } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With