ThreadPoolExecutor在执行过程中出现异常

前言

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

线程池提交的方式

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

submit

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

java 复制代码
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里面的异常信息,但是整个线程池还在继续运行,并没有停下了;

execute

再用execute方法提交;

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

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

总体执行结果

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

线程池执行源码分析

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

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

接下来addWorker方法里面有个start方法;

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

执行Exceute方法断点

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

执行Submit方法断点

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

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

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

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

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

Demo分析

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

java 复制代码
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

执行结果 统一异常处理;

总结

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

相关推荐
遇见你真好。2 分钟前
递归获取部门数方法
java·springboot
拖拉机3 分钟前
Python(八)类(下)
后端·python
D龙源8 分钟前
VSCode进程间通信构建过程详解
后端·架构
开心就好202510 分钟前
【Flutter】Flutter 打开第三方应用 ( url_launcher 插件搜索与安装 | url_launcher 插件官方示例 | 打开浏览器 |
后端
海拥18 分钟前
突破反爬限制的智能数据采集实战 —— 面向中小企业的高效信息监控方案
后端
uhakadotcom24 分钟前
Google Cloud IoT 平台入门:基础架构与示例详解
后端·面试·github
雷渊25 分钟前
ES不支持decimal,如何避免丢失精度?
java·后端·面试
雷渊26 分钟前
ES支持乐观锁吗?如何实现的?
java·后端·面试
乐维_lwops33 分钟前
Tomcat大版本升级教程
java·tomcat
雷渊33 分钟前
ES支持哪些数据类型,和mysql之间的映射关系是怎么样的?
java·后端·面试