前言:
不知大家在对待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,是一个没有意义的成功次数标识,所以你不能用这个成功次数去作为之后的业务执行逻辑,或者作为返回给用户的精准数据。