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.
Here is an example of executing a Runnable with an ExecutorService : ExecutorService executorService = Executors. newSingleThreadExecutor(); executorService. execute(new Runnable() { public void run() { System.
We use the Executors. newSingleThreadExecutor() method to create an ExecutorService that uses a single worker thread for executing tasks. If a task is submitted for execution and the thread is currently busy executing another task, then the new task will wait in a queue until the thread is free to execute it.
ExecutorService is a JDK API that simplifies running tasks in asynchronous mode. Generally speaking, ExecutorService automatically provides a pool of threads and an API for assigning tasks to it.
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.
Exceptions which are thrown by tasks submitted to ExecutorService#submit
get wrapped into an ExcecutionException
and are rethrown by the Future.get()
method. This is, because the executor considers the exception as part of the result of the task.
If you however submit a task via the execute()
method which originates from the Executor
interface, the UncaughtExceptionHandler
is notified.
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