likes
comments
collection
share

ThreadPoolExecutor在执行过程中出现异常

作者站长头像
站长
· 阅读数 11

前言

在开发过程中处理异步任务的时候,我们基本都会使用自定义的线程池,进行异步任务的处理,使用自定义的线程池处理任务的好处,无非就是降低资源消耗、提高响应速度,这里就不做过多的解释说明了,主要来探讨下,如果异步任务中出现异常,线程池是继续运行还是停下来报错呢;

线程池提交的方式

线程池提交的两种方式,与一种是submit,一种是execute,通过创建线程池来分别使用两种提交方式,并且在提交的过程中抛出异常,看看会有什么区别;

submit

先用submit提交,返回Future对象,然后通过get方法获取到我们自己抛出的异常;

private final static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 0l, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<>(1000));

public static void main(String[] args) throws InterruptedException, ExecutionException {

    Future<Object> feature = (Future<Object>) executor.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("submit 方式提交异常");
            throw new RuntimeException("submit提交方式执行异常");
        }
    });

    feature.get();
}

执行结果可能看到,当前线程不会中止,我们通过返回的Future对象,并且调用了get方法获取到了存在RunnableFuture里面的异常信息,但是整个线程池还在继续运行,并没有停下了;

ThreadPoolExecutor在执行过程中出现异常

execute

再用execute方法提交;

executor.execute(new Runnable() {
    @Override
    public void run() {
        System.out.println("execute方式提交异常");
        throw new RuntimeException("execute提交方式执行异常");
    }
});

执行结果可以看到,当前线程会中止,控制台自己抛出了异常,线程池依旧还是在运行,没有听停止掉;

ThreadPoolExecutor在执行过程中出现异常

总体执行结果

线程池出现异常后,并不会让整个线程池停下来,不同的是submit的执行如果出现异常当前线程不会被中止,而execute执行出现异常会让当前线程中止并且抛出异常;

线程池执行源码分析

Submit方法的实现还是调用了execute方法,将RunnableFuture对象提交给了 ThreadPoolExecutor;

ThreadPoolExecutor在执行过程中出现异常

Execute方法,有个判断如果当前活跃线程小于核心线程,就会调用addWorker创建线程Worker的核心方法,这个方法的具体细节就不做过多的解释了,有兴趣的小伙伴可以自己去看下源码;

ThreadPoolExecutor在执行过程中出现异常 接下来addWorker方法里面有个start方法;

ThreadPoolExecutor在执行过程中出现异常

ThreadPoolExecutor里面有个Worker内部类,这个Worker内部类主要是进行工作线程的创建,通过ThreadFactory创建,这个this就是指的上面addWorker方法里面的”t”;

ThreadPoolExecutor在执行过程中出现异常

执行Exceute方法断点

Worker类的runWorker方法执行工作线程,我们在task.run方法这边打一个断点,我们执行上面的execute方法看看 ,可以看到外面提交的异常在这里得到了捕获,在catch重新抛出了异常,这里进行了捕获意味了worker线程被中止;

ThreadPoolExecutor在执行过程中出现异常

执行Submit方法断点

再次回到submit里面的newTaskFor方法,这个方法返回了FutureTask对象,FutureTask是实现了RunnableFuture接口所以也会执行run方法;

ThreadPoolExecutor在执行过程中出现异常

执行submit方法后发现,在catch里面并没有将这个异常进行抛出,而是调用了setException方法,Callable对象的call方法被try/catch包住了;

ThreadPoolExecutor在执行过程中出现异常

setException方法用outcome接收了异常t,

ThreadPoolExecutor在执行过程中出现异常

FutureTask的get方法,调用了report方法;

ThreadPoolExecutor在执行过程中出现异常

所以只有调用FutureTask.get()的时候才能获取到异常信息;

ThreadPoolExecutor在执行过程中出现异常

Demo分析

上面关于线程池提交任务的demo,我们在提交的时候执行了try/catch代码块,这种写法非常繁琐,我们其实可以使用ThreadFactory,自定义线程工厂,在自定义的线程工厂里面统一管理线程池的异常处里;

public class CustomThreadFactory implements ThreadFactory {


    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final AtomicInteger threadNumber = new AtomicInteger(1);
     private final String namePrefix ="pool-" +
            poolNumber.getAndIncrement() +
            "-thread-";
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement()) {
            @Override
            public void run() {
                try {
                    super.run();
                } catch (Exception e) {
                    System.out.println("异常信息:" + e.getMessage());
                }
            }
        };
        return t;
    }
}

线程池定义加入自定义CustomThreadFactory

ThreadPoolExecutor在执行过程中出现异常

执行结果 统一异常处理;

ThreadPoolExecutor在执行过程中出现异常

总结

只有了解了线程池提交任务的步骤,以及异常的处理,才能让我们在使用线程池的时候出现异常,能够及时处理,避免线程池提交任务处理失败导致数据丢失;

转载自:https://juejin.cn/post/7346460401829773349
评论
请登录