How to catch exceptions in FutureTask

Viewed 27130

After finding that FutureTask running in a Executors.newCachedThreadPool() on Java 1.6 (and from Eclipse) swallows exceptions in the Runnable.run() method, I've tried to come up with a way to catch these without adding throw/catch to all my Runnable implementations.

The API suggests that overriding FutureTask.setException() should help in this:

Causes this future to report an ExecutionException with the given throwable as its cause, unless this Future has already been set or has been cancelled. This method is invoked internally by the run method upon failure of the computation.

However this method doesn't seem to be called (running with the debugger shows the exception is caught by FutureTask, but setException isn't called). I've written the following program to reproduce my problem:

public class RunTest {
    public static void main(String[] args) {
        MyFutureTask t = new MyFutureTask(new Runnable() {

            @Override
            public void run() {
                throw new RuntimeException("Unchecked exception");

            }
        });

        ExecutorService service = Executors.newCachedThreadPool();
        service.submit(t);
    }
}

public class MyFutureTask extends FutureTask<Object> {

    public MyFutureTask(Runnable r) {
        super(r, null);
    }

    @Override
    protected void setException(Throwable t) {
        super.setException(t);
        System.out.println("Exception: " + t);
    }
}

My main question is: How can I catch Exceptions thrown in a FutureTask? Why doesn't setException get called?

Also I would like to know why the Thread.UncaughtExceptionHandler mechanism isn't used by FutureTask, is there any reason for this?

5 Answers

There are three standard ways and one improvised way. 1. use UncaughtExceptionHandler, set the UncaughtExceptionHandler for the created thread as

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable ex) {..}}

*But the limitation is it catches the exception thrown by thread but in case of future task, it is swallowed. 2. use afterExecute after making a custom threadpoolexecutor with hook that has been provided specially for this purpose. Looking through the code of ThreadpoolExecutor, via submit > execute (there is a workQueue, workQueue.offer), the tasks are added to the work queue

   final void runWorker(Worker arg0) {
  Thread arg1 = Thread.currentThread();
  Runnable arg2 = arg0.firstTask;
  ..
     while(arg2 != null || (arg2 = this.**getTask()**) != null) {
        arg0.lock();
        ..
        try {
           this.beforeExecute(arg1, arg2);
           Object arg4 = null;
           try {
              arg2.run();
           } catch (RuntimeException arg27) {
             ..
           } finally {
              this.**afterExecute**(arg2, (Throwable)arg4);
           }

  }

getTask() {..
 this.workQueue.**poll**();
..}
  1. Then, the third is using simple try catch inside the call method but you can not catch the exception outside here.

  2. The workaround is calling all the call methods from a call method of a TaskFactory, a factory that releases callables.

Related