ScheduledExecutorService的UncaughtExceptionHandler为何无法生效?
为什么你的线程未捕获异常处理器没生效?
问题核心在于ScheduledThreadPoolExecutor的任务包装机制:
你通过schedule提交的Runnable会被线程池自动包装成FutureTask实例。而FutureTask的run()方法内部做了全局异常捕获——所有Throwable都会被它拦截并存储起来,不会让异常传播到你通过ThreadFactory设置的UncaughtExceptionHandler中。只有当你主动调用ScheduledFuture.get()方法时,这些被存储的异常才会被重新抛出。
简单说,工作线程执行的是FutureTask的逻辑,不是你直接提交的Runnable,异常根本没机会触发线程的未捕获异常处理器。
解决方法
如果你不想用try-catch或主动调用get(),可以自定义ScheduledThreadPoolExecutor并重写afterExecute方法来捕获异常:
Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.err.println("Exception in thread "+t.getName()+":"); e.printStackTrace(); } }; ThreadFactory tf = new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setUncaughtExceptionHandler(handler); return t; } }; // 自定义线程池,重写afterExecute ScheduledExecutorService ses = new ScheduledThreadPoolExecutor(1, tf) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); // 当t为null时,异常可能被FutureTask存储了,需要通过get()取出 if (t == null && r instanceof Future<?>) { try { ((Future<?>) r).get(); } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } // 触发自定义的异常处理器 if (t != null) { handler.uncaughtException(Thread.currentThread(), t); } } }; ses.schedule(()->Objects.requireNonNull(null), 100, TimeUnit.MILLISECONDS);
这样就能在不依赖get()的情况下,捕获到任务中的未检查异常。
内容的提问来源于stack exchange,提问作者CoderBendl
相关产品推荐
相关产品推荐

