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

在使用 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 方法,你还是无法得到未捕获异常信息。对于这一点区别,你需要额外注意

转载于:https://my.oschina.net/lifany/blog/197136

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值