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

屏幕快照 2024-03-14 下午1.31.57.png

execute

再用execute方法提交;

 

csharp

复制代码

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

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

屏幕快照 2024-03-14 下午1.20.14.png

总体执行结果

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

线程池执行源码分析

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

屏幕快照 2024-03-14 下午1.38.51.png

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

屏幕快照 2024-03-14 下午1.48.25.png

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

屏幕快照 2024-03-14 下午1.53.15.png

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

屏幕快照 2024-03-14 下午1.57.12.png

执行Exceute方法断点

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

30CF1183-E23F-4BFD-BCAF-CE572CD1FA58.png

执行Submit方法断点

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

12DA56F8-28DF-4EE6-9660-ED1609B6DB7D.png

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

6D875DF9-EAC0-4944-802D-84A03F60466A.png

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

C44E8095-6200-4640-BB3F-C83BB1F21B59.png

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

6F44E807-5BAB-42C9-A089-A83C1BA953ED.png

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

3FA26766-3C24-470B-857A-CA8E18C55764.png

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

A552AFF2-0FFB-49D9-B31A-AC0F204A62F0.png

执行结果 统一异常处理;

90E9F4AD-5E2C-4663-8EFD-B9106B18E31E.png

总结

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

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值