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

执行结果 统一异常处理;

总结

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

相关推荐
路在脚下@20 分钟前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
啦啦右一21 分钟前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien23 分钟前
Spring Boot常用注解
java·spring boot·后端
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
菠萝蚊鸭2 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl