Java 并发之线程池未捕获异常信息丢失的问题

原创
2014/02/03 22:37
阅读数 3.7K

在使用 JUC 的线程池时(ThreadPoolExecutor 等),需要注意执行任务时可能会抛出的各种 RuntimeException。如果直接使用 JDK 提供的线程池实现的时候,很有可能发生任务“悄无声息”、“莫名其妙”地就结束了。其实,这种情况的发生是因为你没用捕获并处理 RunnableCallable 中发生的异常。

在你写命令行程序的时候,上述情况并不会发生。那是因为线程的默认 UncaughtExceptionHandler 会将异常栈信息输出到命令行界面上,所以大家都知道任务因为异常的发生而退出。但是,在服务器应用中,没有人会一直去看控制台输出。这时,如果还是使用默认的 UncaughtExceptionHandler 就不合适了。

如何解决未捕获异常信息丢失的问题

方法一:自定义 ThreadFactory

话不多说,直接看代码

public class LogExceptionThreadFactory implements ThreadFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger("DEFAULT LOGGER");
    private static final ThreadFactory DEFAULT_THREAD_FACTORY = Executors.defaultThreadFactory();

    @Override
    public Thread newThread(Runnable r) {
        if (r == null) throw new NullPointerException();

        Thread t = DEFAULT_THREAD_FACTORY.newThread(r);

        t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                LOGGER.error("Uncaught Exception:", e);
            }
        });

        return t;
    }
}

然后你可以通过 Executors 中的工具方法使用这个自定义的 ThreadFactory

ExecutorService executorService = Executors.newSingleThreadExecutor(new LogExceptionThreadFactory());

方法二:继承 ThreadPoolExecutor,Override newTaskFor 方法

从 JDK 6 开始,AbstractExecutorService 中就加入了一个新方法 newTaskFor。通过这个工厂方法,你可以对传入的 RunnableCallable 对象做一些封装。默认的实现是封装成 FutureTask。为了解决未捕获异常丢失的问题,你一再封装一层,在调用 runcall 方法之外捕获异常并处理。

PS. ThreadPoolExecutorexecutesubmit 方法在未捕获异常处理方面的不同

如果按照方法一,在调用 execute 方法时,未捕获的运行时异常信息就会被处理。但是如果是用 submit 方法,异常信息会被封装到 FutureTask.get() 方法所抛出的 ExecutionException 中。也就是说,如果使用 submit 不调用 FutureTask.get 方法,你还是无法得到未捕获异常信息。对于这一点区别,你需要额外注意

展开阅读全文
打赏
0
15 收藏
分享
加载中
更多评论
打赏
0 评论
15 收藏
0
分享
返回顶部
顶部