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

执行结果 统一异常处理;

总结

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

相关推荐
喵叔哟1 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生7 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
郑祎亦30 分钟前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
不是二师兄的八戒30 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生42 分钟前
Easyexcel(2-文件读取)
java·excel
本当迷ya43 分钟前
💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)
后端·程序员
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
计算机毕设指导62 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study2 小时前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言
Chris _data2 小时前
二叉树oj题解析
java·数据结构