views:

233

answers:

2

Hi, I've stumbled upon a problem, that can be summarized as follows:

When I create the thread manually (i.e. by instantiating java.lang.Thread) the UncaughtExceptionHandler is called appropriately. However, when I use an ExecutorService with a ThreadFactory the handler is ommited. What did I miss?

public class ThreadStudy {

private static final int THREAD_POOL_SIZE = 1;

public static void main(String[] args) {

 // create uncaught exception handler

 final UncaughtExceptionHandler exceptionHandler = new UncaughtExceptionHandler() {

  @Override
  public void uncaughtException(Thread t, Throwable e) {
   synchronized (this) {
    System.err.println("Uncaught exception in thread '" + t.getName() + "': " + e.getMessage());
   }
  }
 };

 // create thread factory

 ThreadFactory threadFactory = new ThreadFactory() {

  @Override
  public Thread newThread(Runnable r) {
   // System.out.println("creating pooled thread");
   final Thread thread = new Thread(r);
   thread.setUncaughtExceptionHandler(exceptionHandler);
   return thread;
  }
 };

 // create Threadpool

 ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE, threadFactory);

 // create Runnable

 Runnable runnable = new Runnable() {

  @Override
  public void run() {
   // System.out.println("A runnable runs...");
   throw new RuntimeException("Error in Runnable");
  }
 };

 // create Callable

 Callable<Integer> callable = new Callable<Integer>() {

  @Override
  public Integer call() throws Exception {
   // System.out.println("A callable runs...");
   throw new Exception("Error in Callable");
  }
 };

 // a) submitting Runnable to threadpool
 threadPool.submit(runnable);

 // b) submit Callable to threadpool
 threadPool.submit(callable);

 // c) create a thread for runnable manually
 final Thread thread_r = new Thread(runnable, "manually-created-thread");
 thread_r.setUncaughtExceptionHandler(exceptionHandler);
 thread_r.start();

 threadPool.shutdown();
 System.out.println("Done.");
}
}

I expect: Three times the message "Uncaught exception..."

I get: The message once (triggered by the manually created thread).

Reproduced with Java 1.6 on Windows 7 and Mac OS X 10.5.

Thank you very much
Mike [;-)

+5  A: 

Because the exception does not go uncaught.

The Thread that your ThreadFactory produces is not given your Runnable or Callable directly. Instead, the Runnable that you get is an internal Worker class, for example see ThreadPoolExecutor$Worker. Try System.out.println() on the Runnable given to newThread in your example.

This Worker catches any RuntimeExceptions from your submitted job.

You can get the exception in the ThreadPoolExecutor#afterExecute method.

Thilo
A: 

I just browsed through my old questions and thought I might share the solution I implemented in case it helps someone (or I missed a bug).

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;


/**
 * @author Mike Herzog, 2009
 */
public class ExceptionHandlingExecuterService extends ScheduledThreadPoolExecutor {

    /** My ExceptionHandler */
    private final UncaughtExceptionHandler exceptionHandler;

    /**
     * Encapsulating a task and enable exception handling.
     * <p>
     * <i>NB:</i> We need this since {@link ExecutorService}s ignore the
     * {@link UncaughtExceptionHandler} of the {@link ThreadFactory}.
     * 
     * @param <V> The result type returned by this FutureTask's get method.
     */
    private class ExceptionHandlingFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {

        /** Encapsulated Task */
        private final RunnableScheduledFuture<V> task;

        /**
         * Encapsulate a {@link Callable}.
         * 
         * @param callable
         * @param task
         */
        public ExceptionHandlingFutureTask(Callable<V> callable, RunnableScheduledFuture<V> task) {
            super(callable);
            this.task = task;
        }

        /**
         * Encapsulate a {@link Runnable}.
         * 
         * @param runnable
         * @param result
         * @param task
         */
        public ExceptionHandlingFutureTask(Runnable runnable, RunnableScheduledFuture<V> task) {
            super(runnable, null);
            this.task = task;
        }

        /*
         * (non-Javadoc)
         * @see java.util.concurrent.FutureTask#done() The actual exception
         * handling magic.
         */
        @Override
        protected void done() {
            // super.done(); // does nothing
            try {
                get();

            } catch (ExecutionException e) {
                if (exceptionHandler != null) {
                    exceptionHandler.uncaughtException(null, e.getCause());
                }

            } catch (Exception e) {
                // never mind cancelation or interruption...
            }
        }

        @Override
        public boolean isPeriodic() {
            return this.task.isPeriodic();
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return task.getDelay(unit);
        }

        @Override
        public int compareTo(Delayed other) {
            return task.compareTo(other);
        }

    }

    /**
     * @param corePoolSize The number of threads to keep in the pool, even if
     *        they are idle.
     * @param eh Receiver for unhandled exceptions. <i>NB:</i> The thread
     *        reference will always be <code>null</code>.
     */
    public ExceptionHandlingExecuterService(int corePoolSize, UncaughtExceptionHandler eh) {
        super(corePoolSize);
        this.exceptionHandler = eh;
    }

    @Override
    protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) {
        return new ExceptionHandlingFutureTask<V>(callable, task);
    }

    @Override
    protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
        return new ExceptionHandlingFutureTask<V>(runnable, task);
    }
}
DerMike