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

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

线程池的异常处理

UncaughtExceptionHandler

我们都知道Runnable接口中的run方法是不允许抛出异常的,因此派生出这个线程的主线程可能无法直接获得该线程在执行过程中的异常信息。如下例:

16ab4be42e380b98f42e548f10de9f1d.png

为什么会这样呢?其实我们看一下Thread中的源码就会发现,Thread在执行过程中如果遇到了异常,会先判断当前线程是否有设置UncaughtExceptionHandler,如果没有,则会从线程所在的ThreadGroup中获取。

注意:每个线程都有自己的ThreadGroup,即使你没有指定,并且它实现了UncaughtExceptionHandler接口。

我们看下ThreadGroup中默认的对UncaughtExceptionHandler接口的实现:

07af44c49253a5b991cb79e94000afe0.png

这个ThreadGroup如果有父ThreadGroup,则调用父ThreadGroup的uncaughtException,否则调用全局默认的Thread.DefaultUncaughtExceptionHandler,如果全局的handler也没有设置,则只是简单地将异常信息定位到System.err中,这就是为什么我们应当在创建线程的时候,去实现它的UncaughtExceptionHandler接口的原因,这么做可以让你更方便地去排查问题。

通过execute提交任务给线程池

回到线程池这个话题,如果我们向线程池提交的任务中,没有对异常进行try...catch处理,并且运行的时候出现了异常,那会对线程池造成什么影响呢?答案是没有影响,线程池依旧可以正常工作,但是异常却被吞掉了。这通常来说不是一个好事情,因为我们需要拿到原始的异常对象去分析问题。

那么怎样才能拿到原始的异常对象呢?我们从线程池的源码着手开始研究这个问题。当然网上关于线程池的源码解析文章有很多,这里限于篇幅,直接给出最相关的部分代码:

27942460cb0fc50f9e55762b8684b72a.png

这个方法就是真正去执行提交给线程池的任务的代码。

这里我们略去其中不相关的逻辑,重点关注第19行到第32行的逻辑,其中第23行是真正开始执行提交给线程池的任务,那么第20行是干什么的呢?其实就是在执行提交给线程池的任务之前可以做一些前置工作,同样的,我们看到第31行,这个是在执行完提交的任务之后,可以做一些后置工作。

beforeExecute这个我们暂且不管,重点关注下afterExecute这个方法。我们可以看到,在执行任务过程中,一旦抛出任何类型的异常,都会提交给afterExecute这个方法,然而查看线程池的源代码我们可以发现,默认的afterExecute是个空实现,因此,我们有必要继承ThreadPoolExecutor去实现这个afterExecute方法。

看源码我们可以发现这个afterExecute方法是protected类型的,从官方注释上也可以看到,这个方法就是推荐子类去实现的。

当然,这个方法不能随意去实现,需要遵循一定的步骤,具体的官方注释也有讲,这里摘抄如下

989fc6cf7423234c9fb4f18642f7a4b0.png

那么通过这种方式,就可以将原先可能被线程池吞掉的异常成功捕获到,从而便于排查问题。

但是这里还有个小问题,我们注意到在runWorker方法中,执行task.run();语句之后,各种类型的异常都被抛出了,那这些被抛出的异常去了哪里?事实上这里的异常对象最终会被传入到Thread的dispatchUncaughtException方法中,源码如下:

03c1dd4f7348d7d7adac85e42ab80fdd.png

可以看到它会去获取UncaughtExceptionHandler的实现类,然后调用其中的uncaughtException方法,这也就回到了我们上一小节所分析的UncaughtExceptionHandler实现的具体逻辑。那么为了拿到最原始的异常对象,除了实现UncaughtExceptionHandler接口之外,也可以考虑实现afterExecute方法。

通过submit提交任务到线程池

这个同样很简单,我们还是先回到submit方法的源码:

acb42ec9e12796a17cedd4ad3e5f636c.png

这里的execute方法调用的是ThreadPoolExecutor中的execute方法,执行逻辑跟通过execute提交任务到线程池是一样的。我们先重点关注这里的newTaskFor方法,其源码如下:

7ba1c3b320bcfd39331d1f71dd32988d.png

可以看到提交的Callable对象用FutureTask封装起来了。我们知道最终会执行到上述runWorker这个方法中,并且最核心的执行逻辑就是task.run();这行代码。我们知道这里的task其实是FutureTask类型,因此我们有必要看一下FutureTask中的run方法的实现:

9844cd14dfe6bb1215c0355f1425a597.png

可以看到这其中跟异常相关的最关键的代码就在第17行,也就是setException(ex);这个地方。我们看一下这个地方的实现:

d1e43babc1336e3e890c0f24de95f542.png

这里最关键的地方就是将异常对象赋值给了outcome,outcome是FutureTask中的成员变量,我们通过调用submit方法,拿到一个Future对象之后,再调用它的get方法,其中最核心的方法就是report方法,下面给出每个方法的源码:

首先是get方法:

427ea7db73546cf6ef07228308646ccd.png

可以看到最终调用了report方法,其源码如下:

5f04bc8317b9c9047fe0db84a0ee247d.png

上面是一些状态判断,如果当前任务不是正常执行完毕,或者被取消的话,那么这里的x其实就是原始的异常对象,可以看到会被ExecutionException包装。因此在你调用get方法时,可能会抛出ExecutionException异常,那么调用它的getCause方法就可以拿到最原始的异常对象了。

综上所述,针对提交给线程池的任务可能会抛出异常这一问题,主要有以下两种处理思路:

  1. 在提交的任务当中自行try...catch,但这里有个不好的地方就是如果你会提交多种类型的任务到线程池中,每种类型的任务都需要自行将异常try...catch住,比较繁琐。而且如果你只是catch(Exception e),可能依然会漏掉一些包括Error类型的异常,那为了保险起见,可以考虑catch(Throwable t)。
  2. 自行实现线程池的afterExecute方法,或者实现Thread的UncaughtExceptionHandler接口。

下面给出我个人创建线程池的一个示例,供大家参考:

BlockingQueue queue = new ArrayBlockingQueue<>(DEFAULT_QUEUE_SIZE); statisticsThreadPool = new ThreadPoolExecutor(DEFAULT_CORE_POOL_SIZE, DEFAULT_MAX_POOL_SIZE, 60, TimeUnit.SECONDS, queue, new ThreadFactoryBuilder() .setThreadFactory(new ThreadFactory() { private int count = 0; private String prefix = "StatisticsTask"; @Override public Thread newThread(Runnable r) { return new Thread(r, prefix + "-" + count++); } }).setUncaughtExceptionHandler((t, e) -> { String threadName = t.getName(); logger.error("statisticsThreadPool error occurred! threadName: {}, error msg: {}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/336915.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式示例_状态设计模式示例

设计模式示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#x…

cam350 不能打开光绘文件_如何在CAM350中导入Allegro光绘

内容简介&#xff1a;本人之前遇到过 CAM350 导入 Allegro 格式光绘后钻孔偏移、异型孔不显示的问题&#xff0c;捣鼓过之后找到一个方法&#xff0c;在 Allegro 群友的建议下制作该文档进行适当的说明。1. 导入 Gerber 文件鉴于手工导入 Gerber 文件需要逐层设置&#xff0c;过…

d3js mysql_D3.js入门指南

近期略有点诸事不顺&#xff0c;趁略有闲余之时&#xff0c;玩起D3.js。之前实际项目中主要是用各种chart如hightchart、echarts等&#xff0c;这些图形库玩起来貌都是完美的&#xff0c;一切皆可配置&#xff0c;但几年前接触了D3之后&#xff0c;觉得前面那chart类库局限的地…

layui templet格式化_layui使用templet格式化表格数据的方法

增加js/*---------------------格式化时间开始--------------------------*///对Date的扩展&#xff0c;将 Date 转化为指定格式的String//月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符&#xff0c;//年(y)可以用 1-4 个占位符&#xff0c;毫秒(S)只能用…

设计模式示例_代理设计模式示例

设计模式示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#x…

mysql insert 字符集_有关 MySQL 字符集的注意事项-爱可生

本文关键字&#xff1a;字符集、建库建表一、数据库和字符集1. 建库时指定创建数据库时&#xff0c;显式指定字符集和排序规则&#xff0c;同时&#xff0c;当切换到当前数据库后&#xff0c;参数 character_set_database&#xff0c;collation_database 分别被覆盖为当前显式指…

c#发送讯息_企业讯息

c#发送讯息本文是我们名为“ EAI的Spring集成 ”的学院课程的一部分。 在本课程中&#xff0c;向您介绍了企业应用程序集成模式以及Spring Integration如何解决它们。 接下来&#xff0c;您将深入研究Spring Integration的基础知识&#xff0c;例如通道&#xff0c;转换器和适…

java cr_WildFly 10 CR 2发布– Java EE 7,Java 8,Hibernate 5,JavaScript支持热重载

java cr昨天&#xff0c;WildFly团队发布了最新版本的WildFly 10 。 CR2很可能是预计于十月份发布最终版本之前的最后一个版本。 即使主要支持的Java EE规范是7&#xff0c;WildFly 8和WildFly 9也提供了许多新功能&#xff0c;而WildFly 9和WildFly 9现在制作了三个服务器版本…

侬娜·杰尔_杰尔·地狱

侬娜杰尔什么是JAR地狱&#xff1f; &#xff08;或者是classpath地狱&#xff1f;还是依赖地狱&#xff1f;&#xff09;在考虑使用Maven或OSGi等现代开发工具时&#xff0c;哪些方面仍然有意义&#xff1f; 有趣的是&#xff0c;似乎没有对这些问题的结构化答案&#xff08;…

Mysql运行在内核空间_思考mysql内核之初级系列6—innodb文件管理 | 学步园

在上一篇里面&#xff0c;bingxi和alex思考了information_schema&#xff0c;这个一直在innodb外围打转。没有进入到innodb的内部。在后续的文章中&#xff0c;以innodb的为主&#xff0c;逐个思考。Bingxi和alex今天了解了fil文件管理。对应的文件为&#xff1a;D:/mysql-5.1.…

pcl_openmap_OpenMap教程第2部分–使用MapHandler构建基本地图应用程序–第1部分

pcl_openmap1.简介 在第一个教程中&#xff0c;我们创建了一个基本的OpenMap GIS应用程序&#xff0c;该应用程序在JFrame中显示一个从文件系统加载的具有一个形状图层的地图。 该教程基于com.bbn.openmap.app.example.SimpleMap 。 在该教程中&#xff0c;我们使用了以下OpenM…

mysql7.5安装教程_CentOS7.5下yum安装MySQL8图文教程

卸载MariaDB1.列出所有安装的MariaDB rpm 包rpm -qa | grep mariadb2.强制卸载rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64安装MySQL1. 环境CentOS7.52. 获取MySQL最新版 rpm包yum仓库下载MySQLyum localinstall https://repo.mysql.com//mysql80-community-release-e…

mysql和sqlserver分页的区别_关于SQLServer和MySQL 查询分页语句区别

首先来定义几个要用到的参数(例子)t_user数据表int currentPage ; //当前页int pageRecord ; //每页显示记录数关于SqlServer数据库分页SQL语句为:String sql "select top "pageRecord " * from t_user where id not in (select top "(currentPage-1)*pag…

java 微型数据库_Java 9代码工具:使用Java微型基准测试工具的实践会话

java 微型数据库用肉眼看&#xff0c;基准测试似乎只是确定执行某些代码需要花费多长时间的简单问题。 但是&#xff0c;通常情况下&#xff0c;这是幼稚的方法。 提供具有准确和可重复结果的有意义的基准并非易事。 在本文中&#xff0c;我们将向您介绍OpenJDK代码工具项目&a…

mysql快速随机_MySQL随机取数据最高效的方法

mysql随机取数据最高效率的方法发现在SQL语句里有一个 ORDER BY rand() 这样的一个语句&#xff0c;这个说是用着方便&#xff0c;但是效率实在是太低了&#xff0c;于是我用了以下的方法来优化&#xff0c;就是用JOIN表的方法来达到这个取随机数据行的方法&#xff0c;你可以用…

部署被测软件应用和中间件_使用FlexDeploy对融合中间件应用程序进行自动化软件测试...

部署被测软件应用和中间件自动化软件测试是任何软件组织都必须执行的强制性活动之一&#xff0c;以保证其产品质量。 但是&#xff0c;此过程通常变得相当复杂&#xff0c;尤其是涉及由多个不同部分组成的现代复杂系统的自动化测试时。 所有这些部分都基于不同的技术&#xff0…

投行数据_投行对Java的二十大核心访谈问答

投行数据这是在金融领域&#xff08;主要是在大型投资银行&#xff09;共享Java核心访谈问题和答案的新系列。 在JP Morgan&#xff0c;Morgan Stanley&#xff0c;Barclays或Goldman Sachs上会问许多这些Java面试问题。 银行主要从多线程 &#xff0c; 集合 &#xff0c;序列化…

primefaces_通过OmniFaces缓存组件以编程方式缓存PrimeFaces图表

primefaces在这篇文章中&#xff0c;您将看到如何结合PrimeFaces和OmniFaces获得可缓存的图表。 为了使事情变得简单&#xff0c;我们将使用PrimeFaces 折线图。 对于这种图表&#xff0c;我们可以在页面中使用<p&#xff1a;chart />标签和一个简单的托管bean。 因此&am…

hmailserver mysql密码_第二步:点晴MIS系统Email模块hMailServer数据库连接配置指引

点击&#xff1a;windows系统-》开始-》所有程序-》hmailserver-》hMailServer Database Setup&#xff0c;在弹出的对话框中输入默认管理密码“clicksun2010”&#xff0c;进入后按照以下步骤操作&#xff1a;输入登陆密码&#xff1a;clicksun2010&#xff0c;务必使用这个密…

spring3.0异步_在Spring 4.2中更简单地处理异步事务绑定事件

spring3.0异步介绍 如您可能已经知道的&#xff08;例如&#xff0c;从我以前的博客文章中 &#xff09;&#xff0c;不再需要创建一个单独的类来实现带有onApplicationEvent方法的ApplicationListener &#xff0c;以便能够对应用程序事件做出响应&#xff08;包括来自Spring …