views:

1000

answers:

4

I'm trying to use Java's ThreadPoolExecutor class to run a large number of heavy weight tasks with a fixed number of threads. Each of the tasks has many places during which it may fail due to exceptions.

I've subclassed ThreadPoolExecutor and I've overridden the "afterExecute" method which is supposed to provide any uncaught exceptions encountered while running a task. However, I can't seem to make it work.

For example:


import java.util.concurrent.*;

public class ThreadPoolErrors extends ThreadPoolExecutor {

    public ThreadPoolErrors() {

        super(  1, // core threads
                1, // max threads
                1, // timeout
                TimeUnit.MINUTES, // timeout units
                new LinkedBlockingQueue() // work queue
        );

    }

    protected void afterExecute(Runnable r, Throwable t) {

        super.afterExecute(r, t);

        if(t != null) {
            System.out.println("Got an error: " + t);
        } else {
            System.out.println("Everything's fine--situation normal!");
        }
    }

    public static void main( String [] args) {

        ThreadPoolErrors threadPool = new ThreadPoolErrors();

        threadPool.submit( 
                new Runnable() {

                    public void run() {

                        throw new RuntimeException("Ouch! Got an error.");

                    }
                }
        );

        threadPool.shutdown();
    }
}

The output from this program is "Everything's fine--situation normal!" even though the only Runnable submitted to the thread pool throws an exception. Any clue to what's going on here?

Thanks!

+3  A: 

If you want to process exceptions thrown by the task, then it is generally better to use Callable rather than Runnable.

Callable.call() is permitted to throw checked exceptions, and these get propagated back to the calling thread:

Callable task = ...
Future future = executor.submit(task);
try {
   future.get();
} catch (ExecutionException ex) {
   ex.getCause().printStackTrace();
}

If Callable.call() throws an exception, this will be wrapped in an ExecutionException and thrown by Future.get().

This is likely to be much preferable to subclassing ThreadPoolExecutor. It also gives you the opportunity to re-submit the task if the exception is a recoverable one.

skaffman
A: 

Instead of subclassing ThreadPoolExecutor, I would provide it with a ThreadFactory instance that creates new Threads and provides them with an UncaughtExceptionHandler

Kevin
I tried this as well, but the uncaughtException method never seems to get called. I believe this is because a worker thread in the ThreadPoolExecutor class is catching the exceptions.
Tom
+3  A: 

From the docs:

Note: When actions are enclosed in tasks (such as FutureTask) either explicitly or via methods such as submit, these task objects catch and maintain computational exceptions, and so they do not cause abrupt termination, and the internal exceptions are not passed to this method.

When you submit a Runnable, it'll get wrapped in a Future.

Your afterExecute should be something like this:

  protected void afterExecute(Runnable r, Throwable t) {
      super.afterExecute(r, t);
      if (t == null && r instanceof Future<?>) {
        try {
          Object result = ((Future<?>) r).get();
        } catch (CancellationException ce) {
            t = ce;
        } catch (ExecutionException ee) {
            t = ee.getCause();
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt(); // ignore/reset
        }
      }
      if (t != null)
               System.out.println(t);
     }
 }
nos
Thanks, I ended up using this solution.Additionally, in case anyone is interested: others have suggested not subclassing the ExecutorService, but I did anyway because I wanted to monitor tasks as they complete rather than waiting for all of them to terminate and then calling get() on all of the returned Futures.
Tom
Another approach to subclassing the executor is to subclass FutureTask and override its 'done' method
nos
Tom >> Can you please post your sample snippet code where you subclassed ExecutorService to monitor tasks as they complete...
HonorGod
+2  A: 

The explanation for this behavior is right in the javadoc for afterExecute:

Note: When actions are enclosed in tasks (such as FutureTask) either explicitly or via methods such as submit, these task objects catch and maintain computational exceptions, and so they do not cause abrupt termination, and the internal exceptions are not passed to this method.

Drew Wills
Good point--some how I missed this.
Tom