假设我们有一个线程池,由于程序需要,我们向该线程池中提交了好多好多任务,但是 这些任务都没有对异常进行try catch处理,并且运行的时候都抛出了异常 。这会对线程池的运行带来什么影响?

正确答案是:没有影响。这可不是好事情。

想一下,如果是你开发了一个线程池供开发者使用,你会不会对这种情况做处理?想想也是肯定的,不然你提供给别人使用的东西就是有问题的,欠考虑的。而且java线程池的主要开发人员是大名鼎鼎的Doug Lea,你觉得他开发的代码怎么会允许出现这种问题?

这个问题很棘手,因为它躺在角落里,程序正常运行的时候,它并不会出来作祟。

问题分析

接下来我们来看一下java中的线程池是如何运行我们提交的任务的,详细流程比较复杂,这里我们不关注,我们只关注任务执行的部分。java中的线程池用的是ThreadPoolExecutor,真正执行代码的部分是runWorker方法:final void runWorker(Worker w)

可以看到,程序会捕获包括Error在内的所有异常,并且在程序最后,将出现过的异常和当前任务传递给afterExecute方法。

而ThreadPoolExecutor中的afterExecute方法是没有任何实现的。

 protected void afterExecute(Runnable r, Throwable t) { }

存在问题

想象下ThreadPoolExecutor这种处理方式会有什么问题?

这样做能够保证我们提交的任务抛出了异常不会影响其他任务的执行,同时也不会对用来执行该任务的线程产生任何影响。

问题就在afterExecute方法上, 这个方法没有做任何处理,所以如果我们的任务抛出了异常,我们也无法立刻感知到。  即使感知到了,也无法查看异常信息。

所以,作为一名好的开发者,是不应该允许这种情况出现的。

如何避免这种问题

思路很简单。

1、在提交的任务中将异常捕获并处理,不抛给线程池。

2、异常抛给线程池,但是我们要及时处理抛出的异常。

直接catch

第一种思路很简单,就是我们提交任务的时候,将所有可能的异常都Catch住,并且自己处理。

说白了就是把业务逻辑都trycatch起来。
但是这种思路的缺点就是:
1)所有的不同任务类型都要trycatch,增加了代码量。
2)不存在checkedexception的地方也需要都trycatch起来,代码丑陋。

线程池实现

第二种思路就可以避免上面的两个问题。

第二种思路又有以下四种实现方式

自定义线程池

自定义线程池,继承ThreadPoolExecutor并复写其afterExecute(Runnable r, Throwable t)方法。

实现Thread.UncaughtExceptionHandler接口

实现Thread.UncaughtExceptionHandler接口,
实现void uncaughtException(Thread t, Throwable e);方法,
并将该handler传递给线程池的ThreadFactory

继承ThreadGroup

覆盖其uncaughtException方法。(与第二种方式类似,因为ThreadGroup类本身就实现了Thread.UncaughtExceptionHandler接口)

尤其注意:上面三种方式针对的都是通过execute(xx)的方式提交任务,如果你提交任务用的是submit()方法,那么上面的三种方式都将不起作用,而应该使用下面的方式

采用Future模式

如果提交任务的时候使用的方法是submit,那么该方法将返回一个Future对象,所有的异常以及处理结果都可以通过future对象获取。
采用Future模式,将返回结果以及异常放到Future中,在Future中处理

总结

文章探讨了从用户层面的代码到线程池层面的各种改造方法,力求让业务代码更加健壮可控。异常处理是java中非常重要的流程,但是线程池的默认操作,会使的这些内容被静悄悄的忽略,这在某些情况下是致命的。

Java线程池「异常处理」正确姿势:有病就得治相关推荐

  1. java异常_Java线程池「异常处理」正确姿势:有病就得治

    假设我们有一个线程池,由于程序需要,我们向该线程池中提交了好多好多任务,但是 这些任务都没有对异常进行try catch处理,并且运行的时候都抛出了异常 .这会对线程池的运行带来什么影响? 正确答案是 ...

  2. JAVA线程池ScheduledExecutorService周期性地执行任务 与单个Thread周期性执行任务的异常处理...

    本文记录: 1,使用ScheduledExecutorService的 scheduleAtFixedRate 方法执行周期性任务的过程,讨论了在任务周期执行过程中出现了异常,会导致周期任务失败. 2 ...

  3. 数值分析 使用c语言 源码_分析源码,学会正确使用 Java 线程池

    在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等.本文主要聚焦在如何正确使用线程池上,以及提供一些实用的建议.文中会 ...

  4. JAVA线程池ScheduledExecutorService周期性地执行任务 与单个Thread周期性执行任务的异常处理

    JAVA线程池ScheduledExecutorService周期性地执行任务 与单个Thread周期性执行任务的异常处理 参考文章: (1)JAVA线程池ScheduledExecutorServi ...

  5. java线程池_Java 并发编程 线程池源码实战

    作者 | 马启航 杏仁后端工程师.「我头发还多,你们呢?」 一.概述 笔者在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写 ...

  6. Golang错误和异常处理的正确姿势

    Golang错误和异常处理的正确姿势 错误和异常是两个不同的概念,非常容易混淆.很多程序员习惯将一切非正常情况都看做错误,而不区分错误和异常,即使程序中可能有异常抛出,也将异常及时捕获并转换成错误.从 ...

  7. 面试必问---Java线程池8大拒绝策略

    前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...

  8. Java 线程池必知的8 大拒绝策略

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | kailing.pub/article/ind ...

  9. 由浅入深理解Java线程池及线程池的如何使用

    前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...

最新文章

  1. 《预训练周刊》第33期:艾伦AI研究所等 | 预训练语言模型的高效分层域适应
  2. 5.4 matllab数据插值案例(机动车刹车距离问题、)
  3. springCloud(22):Eureka总结提升
  4. 单核工作法19:给创意充电(上)
  5. 倍增:st表(模板)(洛谷P3865)
  6. Linux 文件区块连续吗,关于Linux文件系统的的简单理解和认识
  7. 当我们群嘲假博士时,不要忘了真博士们的艰辛
  8. 2年6个月11天,外包到阿里的修仙之路
  9. php替代eval_PHP:需要eval()的替代方法来动态构建多维数组
  10. oracle 如何创建游标,Oracle--plsql游标创建和使用
  11. 微信小程序php java_PHP实现微信小程序用户授权的工具类
  12. jquery完善的处理机制
  13. USACO 2.1 海明码(DFS)
  14. 一步一步学习PHP(4)——函数
  15. 话单分析账单分析行踪分析三合一数据分析,即可话单、账单、行踪、涉税数据独立分析,也可混合分析。
  16. I/O多路复用select服务器
  17. 5G相比LTE 大的差异
  18. 20种让你更高效的科学学习方法
  19. 程序员可选择的个博客论坛网站
  20. twig php函数,TWIG的 function 学习

热门文章

  1. win10下,更改程序磁贴图标
  2. 9 迭代器与组合模式
  3. 不小心删了(或覆盖了)window系统变量的PATH的怎么办?
  4. C++实现求解最长公共子序列(LCS)问题【动态规划】
  5. 拼团系统开发的亮点与核心功能
  6. JAVA电商 B2B2C商城系统 多用户商城系统 直播带货 新零售商城 o2o商城 电子商务 拼团商城 分销商城 直播商城 短视频商城 springcloud商城 spring cloud商城
  7. 2023年,哪些Web3赛道的表现最值得期待?(文末有奖)
  8. 拒绝服务攻击过程详解
  9. 如何将数据从旧PC传输到新Mac
  10. 3.3-上位机与下位机通信构架源代码编写与使用方法说明