线程池excutor submit 两种方式 对于异常的处理问题的探究

前言:

  不知大家在对待excutor or sumbit方式执行的线程池,内部出现异常是否要catch住这个问题有没有思考?

例如,我常在代码中发现以下处理方式:

  1、在submit执行的线程池中,catch住所有异常,然后submit方法返回true。最后通过执行线程池的get()方法来获取执行结果成功次数,并且把这个成功次数返回给用户(想想是否这种方法其实是没有用的,不会有异常,所有的线程执行都会成功,这个是个假的成功次数)

ArrayList<Future<Boolean>> futures = Lists.newArrayList();
for (Target target : targetList) {
    Future<Boolean> isSuccess = threadPoolExecutor.submit(() -> {
        // 每一个线程要做的事情
        try {
             dosomething(target);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        //catch 住异常 且返回成功
        return true;
    });
    futures.add(isSuccess);
}
// 使用get去得到submit执行结果
long count = futures.stream().map(s -> {
    try {
        return s.get();
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        return false;
    }
}).filter(s -> s).count();
//模拟返回给用户提示信息 想想这个笔数是否是一个假的笔数呢?是否都是成功的呢?
log.info("当前任务执行完成的笔数{}", count);

  2、在submit执行的线程池中,不刻意catch住异常,最后通过执行get()方法来获取执行结果

  如果你也曾经看到过这样的处理方式,我推荐你看下我的这篇文章。

结论先行

结论一:

  发生异常,excutor会开启新线程处理(即使超过了threadPoolExecutor设置的线程池上限)


// 1、线程池核心 最大线程数都限制为1个
// 2、线程池执行五次任务,其中一个任务抛出异常去中断该线程
// 3、探究剩余四次任务线程池执行逻辑

// 结论:线程池即使设置为只有1个容量,一个线程因为异常中断后,仍然会再开新的线程,去处理剩余任务

ThreadPoolExecutor pool = new ThreadPoolExecutor( 1, 1,
                                                 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

// 五个循环 
IntStream.rangeClosed(1, 5).forEach(i -> pool.execute(() -> {

    log.info("开始:" + Thread.currentThread().getName());
    if (i == 1) {
        throw new RuntimeException("异常抛出"); 
    }
    log.info("结束:执行第" + i + "次,"  + Thread.currentThread().getName());
}));

//执行结果 新开了 pool-1-thread-2去执行剩余4次任务
[pool-1-thread-1] INFO com.example.mytest.exceptionTest.futureExcutorException - 开始:pool-1-thread-1
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 开始:pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 结束:执行第2次,pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 开始:pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 结束:执行第3次,pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 开始:pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 结束:执行第4次,pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 开始:pool-1-thread-2
    [pool-1-thread-2] INFO com.example.mytest.exceptionTest.futureExcutorException - 结束:执行第5次,pool-1-thread-2

结论二:

   发生异常,submit不会报错(表面看来,异常被吞掉了)

ThreadPoolExecutor pool = new ThreadPoolExecutor( 1, 1,
                                                 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
//提交任务执行
List<Future<Boolean>> tasks = IntStream.rangeClosed(1, 4).mapToObj(i -> pool.submit(() -> {
    if (i == 2) {
        throw new RuntimeException("异常抛出");
    }
    return true;
})).collect(Collectors.toList());

// 执行结果 异常不会报错
...

原因简单探究

问题一:excutor是在哪一步去开启新线程处理?
在这里插入图片描述

问题一:submit不会报错(表面上看起来异常被吞掉了)的原因是为啥?
在这里插入图片描述

总结代码中可能存在的问题

   threadPoolExecutor.sumbit (带返回值),手动catch住异常且返回了ture,是一个没有意义的成功次数标识,所以你不能用这个成功次数去作为之后的业务执行逻辑,或者作为返回给用户的精准数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用线程池提交任务时,可以通过以下几种方式处理异常: 1. 使用try-catch语句块捕获异常:在任务的执行代码中使用try-catch语句块捕获异常,并在catch块中处理异常情况。例如: ```java ExecutorService executorService = Executors.newFixedThreadPool(5); executorService.submit(() -> { try { // 任务执行代码 } catch (Exception e) { // 异常处理逻辑 } }); ``` 2. 使用Future对象获取异常:通过使用`submit`方法返回的`Future`对象,可以在任务执行完成后获取任务执行的结果和异常信息。可以使用`Future.get()`方法来获取任务执行的结果,如果任务抛出了异常,`get()`方法会将异常重新抛出。例如: ```java ExecutorService executorService = Executors.newFixedThreadPool(5); Future<?> future = executorService.submit(() -> { // 任务执行代码 }); try { future.get(); // 获取任务执行结果 } catch (ExecutionException e) { Throwable cause = e.getCause(); // 获取任务抛出的异常 // 异常处理逻辑 } ``` 3. 自定义异常处理器:可以实现`Thread.UncaughtExceptionHandler`接口来自定义处理未捕获异常的逻辑。通过设置线程池的默认未捕获异常处理器,可以统一处理线程池中任务抛出的未捕获异常。例如: ```java ExecutorService executorService = Executors.newFixedThreadPool(5); executorService.execute(() -> { // 任务执行代码 }); Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { // 自定义异常处理逻辑 }); ``` 这些是处理线程池任务提交时的异常的一些常见方法,你可以根据具体情况选择适合自己的方式处理异常

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值