为什么UncaughtExceptionHandler不能被ExecutorService调用?
我偶然发现了一个问题,可以概括如下:
当我手动创build线程(即通过实例化java.lang.Thread
) UncaughtExceptionHandler
被适当地调用。 但是,当我用一个ThreadFactory
使用ExecutorService
处理程序是ommited。 我错过了什么?
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."); } }
我期望:三次消息“未捕获的exception…”
我得到:消息一次(由手动创build的线程触发)。
在Windows 7和Mac OS X 10.5上使用Java 1.6进行了转换。
因为exception不会被取消。
ThreadFactory生成的线程没有直接给你的Runnable或Callable。 相反,你得到的Runnable是一个内部的Worker类,例如见ThreadPoolExecutor $ Worker。 尝试在你的例子中赋给newThread的Runnable的System.out.println()
。
此工作人员捕获您提交的作业中的任何RuntimeException。
你可以在ThreadPoolExecutor#afterExecute方法中得到exception。
提交给ExecutorService#submit
的任务抛出的exception被包装成一个Future.get()
方法Future.get()
。 这是因为执行者认为exception是任务结果的一部分。
如果您通过源自Executor
接口的execute()
方法提交任务,则会通知UncaughtExceptionHandler
。
从书中的Java并发实践 (163页)引用,希望这有助于
有点混淆的是,任务抛出的exception使它只能用于未被捕获的exception处理程序,只用于执行提交的任务; 对于通过提交提交的任务,任何抛出的exception(被检查与否)都被认为是任务返回状态的一部分。 如果提交提交的任务终止并产生一个exception,Future.get就会重新生成一个ExecutionExceptionexception。
这是一个例子:
public class Main { public static void main(String[] args){ ThreadFactory factory = new ThreadFactory(){ @Override public Thread newThread(Runnable r) { // TODO Auto-generated method stub final Thread thread =new Thread(r); thread.setUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { // TODO Auto-generated method stub System.out.println("in exception handler"); } }); return thread; } }; ExecutorService pool=Executors.newSingleThreadExecutor(factory); pool.execute(new testTask()); } private static class testTask implements Runnable { @Override public void run() { // TODO Auto-generated method stub throw new RuntimeException(); } }
我使用execute来提交任务和控制台输出“在exception处理程序”
我刚刚浏览了我的旧问题,并认为我可能会分享我实施的解决scheme,以防止某人(或者我错过了一个错误)。
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); } }
有一点解决方法。 在你的run方法中,你可以捕获每一个exception,然后再做这样的事情
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex); //or Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex);
这将“注册”当前的exception抛出到你的uncoughtExceptionHandler或defualt未认可的exception处理程序。 您总是可以重新抛出池worker的捕获exception。
除了Thilos答案:我已经写了一篇关于这个行为的文章,如果你想让它解释的更详细些: https : //ewirch.github.io/2013/12/a-executor-is-not -a-thread.html