“线程池中线程异常后:销毁还是复用?”

目录

一、验证execute提交线程池中

测试

结论

二、验证submit提交线程池中

测试

结论

三、源码解析

查看submit方法的执行逻辑

查看execute方法的执行逻辑

为什么submit方法,没有创建新的线程,而是继续复用原线程?

四、总结


需要说明,本文的线程池都是java.util.concurrent.ExecutorService线程池,本文将围绕验证,阅读源码俩方面来解析这个问题。

一、验证execute提交线程池中

测试

我们首先定义了一个 ThreadPoolExecutorDeadTest 类,用来演示 Java 中 ThreadPoolExecutor 的使用,以及在任务执行过程中发生异常时线程池的行为。然后,在代码中,通过创建一个线程池并提交多个任务来模拟线程池的运行情况,其中一个任务故意抛出异常。

测试代码如下:

public class ThreadPoolExecutorDeadTest {public static void main(String[] args) throws InterruptedException {ExecutorService executorService = buildThreadPoolExecutor();executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute-exception"));executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute"));Thread.sleep(5000);System.out.println("再次执行任务=======================");executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute"));executorService.execute(() -> exeTask("execute"));}public static ExecutorService buildThreadPoolExecutor() {return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build(), new ThreadPoolExecutor.CallerRunsPolicy());}private static void exeTask(String name) {String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]";if ("execute-exception".equals(name)) {throw new RuntimeException(printStr + ", 我抛异常了");} else {System.out.println(printStr);}}
}

首先,调用 buildThreadPoolExecutor() 方法创建一个线程池并赋值给 executorService。这个线程池具有核心线程数 5,最大线程数 10,空闲线程存活时间 30 秒,任务队列容量为 1000,使用 CallerRunsPolicy 作为拒绝策略。 

然后,调用 executorService.execute() 方法多次向线程池提交任务。任务类型为 Runnable,任务的内容是调用 exeTask 方法。

主线程在前五个任务提交后,调用 Thread.sleep(5000) 暂停5秒,以模拟任务执行间隔。在暂停后,向线程池再次提交五个任务,继续执行。

运行结果如下, 

可以看到test-2线程不见了,出现了test-3线程。

结论

execute 提交到线程池的方式,如果执行中抛出异常,并且没有在执行逻辑中catch,那么会抛出异常,并且移除抛出异常的线程,创建新的线程放入到线程池中。

二、验证submit提交线程池中

测试

测试代码:

public class ThreadPoolExecutorDeadTest {public static void main(String[] args) throws InterruptedException {ExecutorService executorService = buildThreadPoolExecutor();executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute-exception"));executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute"));Thread.sleep(5000);System.out.println("再次执行任务=======================");executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute"));executorService.submit(() -> exeTask("execute"));}public static ExecutorService buildThreadPoolExecutor() {return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build(), new ThreadPoolExecutor.CallerRunsPolicy());}private static void exeTask(String name) {String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]";if ("execute-exception".equals(name)) {throw new RuntimeException(printStr + ", 我抛异常了");} else {System.out.println(printStr);}}
}

运行结果如下,

可以看到test-2线程执行异常了,并没有抛出异常栈而且继续复用了,没有被移除。 

结论

submit 提交到线程池的方式,如果执行中抛出异常,并且没有catch,不会抛出异常,不会创建新的线程

 

三、源码解析

查看submit方法的执行逻辑

我们从上述测试代码中的executorService.submit(() -> exeTask("execute"));,进入到 java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)方法,可以看到,submit方法,也是包装了RunnableFuture后调用execute方法。

查看execute方法的执行逻辑

进入execute方法,然后进入execute方法的实现方法,就可以进入到java.util.concurrent.ThreadPoolExecutor#runWorker方法,

可以发现,run方法抛出异常之后,会执行finally。

然后,我们进入到 java.util.concurrent.ThreadPoolExecutor#processWorkerExit方法,

可以发现,如果抛出异常,会移除抛出异常的线程,创建新的线程。

为什么submit方法,没有创建新的线程,而是继续复用原线程?

还记得,我们在查看submit方法的执行逻辑的时候,发现submit也是调用了execute方法,但是在调用之前,包装了一层 RunnableFuture,那一定是在RunnableFuture的实现 FutureTask中有特殊处理了,我们查看源码可以发现。

进入 FutureTask的run方法,

可以发现包装的一层task,catch了异常,并没有往上抛。所以不会移除抛出异常的线程,创建新的线程。

但是,我们通过java.util.concurrent.FutureTask#get(),就可以获取对应的异常信息。

 

四、总结

当一个线程池里面的线程异常后:

  • 当执行方式是execute()时,可以看到堆栈异常的输出,线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。

  • 当执行方式是submit()时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常,不会把这个线程移除掉,也不会创建新的线程放入到线程池中。

以上俩种执行方式,都不会影响线程池里面其他线程的正常执行。

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

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

相关文章

【UML建模】时序图的那点事

【UML建模】时序图的那点事 开篇词&#xff1a;干货篇&#xff1a;1.概述2.时序图的组成元素2.1角色&#xff08;Actor&#xff09;&#xff1a;2.2生命线&#xff08;Lifeline&#xff09;&#xff1a;2.3消息&#xff08;Message&#xff09;&#xff1a;2.4激活条&#xff0…

【Day07】

目录 MySQL-DQL- 基本查询 MySQL-DQL- 条件查询 MySQL-DQL- 聚合函数 MySQL-DQL- 分组查询 MySQL-DQL- 排序查询 MySQL-DQL- 分页查询 MySQL-DQL- 案例 MySQL-多表设计-一对多 MySQL-多表设计-一对多-外键约束 MySQL-多表设计-一对一&多对多 MySQL-多表设计-案例…

学习日志8.30--防火墙NAT

目录 一、实验环境配置 二、配置防火墙静态NAT一对一 三、配置防火墙静态NAT多对多 四、配置防火墙NAT端口转换NAPT 五、防火墙smart-nat、easyip 六、防火墙三元组NAT 在学习过基于路由器的NAT网络地址转换&#xff0c;现在学习基于防火墙NAT的网络地址转换&#xff0c;…

模型 7S分析法(麦肯锡)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。组织全面诊断&#xff0c;战略协同优化。 1 7S分析法(麦肯锡)的应用 1.1 邮储银行的转型&#xff1a;基于麦肯锡7S模型的竞争力提升 中国邮储银行面临着激烈的金融行业竞争&#xff0c;为了迅速提升…

# 利刃出鞘_Tomcat 核心原理解析(十一)-- Tomcat 附加功能 WebSocket -- 2

利刃出鞘_Tomcat 核心原理解析&#xff08;十一&#xff09;-- Tomcat 附加功能 WebSocket – 2 一、Tomcat专题 - WebSocket - 案例 - 登录功能 1、在项目 dzs168_chat_room 中&#xff0c;导入 tomcat 项目依赖&#xff08; dzs168_chat_room/web/lib/ &#xff09; idea -…

数据结构与算法---排序算法

文章目录 排序选择排序冒泡排序插入排序 希尔排序归并排序快速排序桶排序计数排序基数排序堆排序 排序 排序是指将一组数据按照特定的规则或顺序进行排列&#xff0c;比如一个数组[1, 5, 2, 4, 3]按照从小到大的顺序排列后就是[1,2,3,4,5]。 排序算法&#xff08;Sorting alg…

全球1km分辨率人口分布栅格数据

我们在《全国省市县三级“七普”人口数据分享》一文中&#xff0c;为你分享过全国人口数据。 现在再为你分享全球1km分辨率人口分布栅格数据&#xff0c;你可以在文末查看该数据的领取方法。 全球1km分辨率人口分布 人口空间分布数据是在各项研究中经常使用的数据&#xff0…

新版IDEA配置前进和后退、打开资源管理器等快捷按钮

新版IDEA&#xff0c;好像是IDEA2024版本开始就默认隐藏了工具条&#xff0c;这时一些很常用的快捷按钮&#xff0c;如前进、后退、打开资源管理器就无法使用。这里图文介绍&#xff0c;如何把这些配置出来。 具体操作如下&#xff1a; 1、选择 File / Settings(windows版)&am…

解决jupyter notebook启动需要密码的问题

解决方法 在运行界面输入 jupyter notebook list 之后运行界面会输出token值&#xff0c;将对应地址后的token复制到密码栏中即可

14 大模型微调-KitTrain

1 介绍 如何降低占用的显存&#xff1a; 梯度累计&#xff1a;在一个完整的模型更新周期&#xff08;epoch&#xff09;中&#xff0c;将多个小批量&#xff08;mini-batches&#xff09;的数据的梯度进行累加&#xff0c;然后在一个较大的批量&#xff08;累积步数&#xff…

测试框架到底是什么,如何定义?

测试框架的关键组件是什么&#xff1f; 测试执行引擎&#xff1a;协调测试的运行、管理序列和报告结果。 测试脚本存储库&#xff1a;存储将要执行的实际测试用例或脚本。 测试数据&#xff1a;测试执行所需的输入数据&#xff0c;可以是静态的、动态的或动态生成的。 存根…

开店到经营,分贝通帮助连锁经营企业这样省钱

如果说大企业的经营核心是做好主营业务的大生意,那么对于连锁经营企业而言,线下门店的一个个小生意,其实也隐藏着“大学问”。费用支出方面,如何从细节处节流,让资金流呈现更良性循环,是连锁经营行业的重要课题。 1、开店前:选址BD全国跑,筐筐发票财务恼 2、日常经营:费用类目…

PMP–冲刺–十大领域易考点三大项目流程敏捷中的角色职责与3个工件高频考点考试技巧

文章目录 十大知识领域易考点--题干关键词一、整合管理二、范围管理三、进度管理四、成本管理五、质量管理六、资源管理七、沟通管理八、风险管理九、采购管理十、干系人管理 考试中的三大项目流程一 、变更流程二 、风险流程三 、收尾流程 敏捷中的角色职责与3个工件--题干关键…

es映射配置(_mapping)

文章目录 1、创建映射字段2、查看映射关系 1、创建映射字段 PUT /索引库名/_mapping {"properties": {"字段名": {"type": "类型","index": true&#xff0c;"store": true&#xff0c;"analyzer": &q…

LVGL | VisualStuio PC模拟器

LVGL | VisualStuio PC模拟器 时间&#xff1a;2024年8月30日17:46:41 文章目录 LVGL | VisualStuio PC模拟器1.参考Visual Studio 版本LVGL版本 2.工程代码3.演示 1.参考 1.16.LVGL&#xff08;UI设计&#xff09;_军事研究员的博客-CSDN博客 2.嵌入式UI开发-lvglwsl2vscode系…

HTML5好看的花店商城源码2

文章目录 1.设计来源1.1 主界面1.2 界面效果11.3 界面效果21.4 界面效果31.5 界面效果41.6 界面效果51.7 界面效果61.8 界面效果71.9 界面效果8 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#…

嵌入式OTG硬件电路分析

大家好,今天主要给大家分享一下,如何使用OTG硬件检测电路,和之前的接口有什么区别。 1. OTG接口与转换器 OTG是"On The Go"的英文缩写,字面上可以理解为“安上即可用”。USB传输是主从结构,一切USB传输都有Host发起。比如在开发板上可以插入U盘,这时开发板作为…

【数据分析预备】Numpy入门

Jupyter Notebook 是一个基于网页的交互式计算环境编写代码、运行代码、查看输出、可视化数据、分享报告文档按单元格运行代码可展示的信息格式更丰富&#xff08;支持Markdown和Latex)交互式运行环境 安装 cmd窗口 pip install notebook启动 jupyter notebook退出 CtrlC …

不平衡分类阈值移动的简单介绍

不平衡分类阈值移动的简单介绍 分类预测模型通常涉及预测类别标签。 尽管如此&#xff0c;许多机器学习算法能够预测类别成员的概率或得分&#xff0c;并且必须对其进行解释&#xff0c;然后才能将其映射到明确的类别标签。这是通过使用阈值&#xff08;例如 0.5&#xff09;…

甜羊浏览器:抖店多店铺管理与自动回复的最佳解决方案

随着短视频平台的蓬勃发展&#xff0c;抖音旗下的电商平台——抖店&#xff0c;已成为许多商家的重要销售渠道。然而&#xff0c;对于拥有多个抖店店铺的商家而言&#xff0c;如何高效管理这些店铺以及处理大量的客户咨询&#xff0c;成为了亟待解决的问题。此时&#xff0c;甜…