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

news/2024/9/18 23:08:27/ 标签: java, 开发语言, 线程池, juc

目录

一、验证execute提交线程池

测试

结论

二、验证submit提交线程池

测试

结论

三、源码解析

查看submit方法的执行逻辑

查看execute方法的执行逻辑

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

四、总结


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

一、验证execute提交线程池

测试

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

测试代码如下:

java">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提交线程池

测试

测试代码:

java">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.ppmy.cn/news/1519202.html

相关文章

经典大语言模型解读(2):生成式预训练的先锋GPT-1

论文地址&#xff1a;Improving Language Understanding by Generative Pre-Training 概述 现实世界中包含了大量的文本语料数据&#xff0c;然而&#xff0c;绝大多数语料都是无标签的。 为了充分利用这些无标签语料库&#xff0c;GPT1.0提出直接利用这些未标记的语料来进行…

VMware Workstation安装及配置CentOS7 Linux操作系统

VMware workstation安装 百度网盘&#xff0c;VMware-workstation-full-17.5.2.exe 安装包&#xff1a; 链接:https://pan.baidu.com/s/1xgbWUlo-hFUbb11MRxIVsw?pwd87bq 提取码: 87bq 检查网络适配器是否正常配置 在VMware Workstation中安装CentOS7 Linux操作系统 下载…

探秘Python字典:解锁数据管理的艺术

引言 字典&#xff08;Dictionary&#xff09;是一种可变容器模型&#xff0c;它可以存储任意类型对象。Python字典使用键-值对&#xff08;key-value pair&#xff09;存储数据&#xff0c;其中键必须是不可变的数据类型如数字、字符串等&#xff0c;而值可以是任何数据类型。…

花店鲜花管理与推荐系统+Python+Django网页界面+管理系统+计算机课设

一、介绍 花店鲜花管理与推荐系统。本系统使用Python作为主要开发语言开发的一个花店鲜花管理与推荐的网站平台。 网站前端界面采用HTML、CSS、BootStrap等技术搭建界面。后端采用Django框架处理用户的逻辑请求&#xff0c;并将用户的相关行为数据保存在数据库中。通过Ajax技…

泰山派的小手机后续(2)

之前 触摸板一直报错的解决&#xff1a; 1 我的触摸板 画错了。 从新画了一个 触摸的转接板&#xff0c;调整过来了。 关于触摸的 线序。 这块屏幕正常 就是横着放的。 关于泰山派接口的线序&#xff1a; 所以我的转接板的画法 是&#xff1a; 开发板与转接板 使用的是 反排线…

AWS 无服务计算服务 Lambda

无服务计算服务 Lambda 引言什么是 AWS Lambda关键特点使用场景 SQS 驱动 Lambda示例场景描述&#xff1a;订单处理系统解决方案&#xff1a;使用 SQS 和 Lambda示例代码&#xff1a;Python Lambda 处理 SQS 消息总结 Lambda ApplicationLambda Application 的主要组成部分创建…

“北京地铁系统中人脸识别技术的安全与效率问题研究”

关于“北京地铁系统中人脸识别技术的安全与效率问题研究”&#xff0c;可以从以下几个方面进行详细分析&#xff1a; 一、人脸识别技术在北京地铁系统中的应用 1. 安检系统 应用背景&#xff1a;为了提升地铁进站安检速度&#xff0c;减少乘客等候时间&#xff0c;北京地铁系…

深度学习速通系列:线性回归vs逻辑回归

线性回归和逻辑回归是两种常用的统计学方法&#xff0c;它们在数据分析和机器学习中扮演着重要的角色。下面是对这两种回归方法的基本介绍&#xff1a; 线性回归&#xff08;Linear Regression&#xff09;&#xff1a; 线性回归是一种预测分析方法&#xff0c;用于建模和分析…

基于单片机的无线空气质量检测系统设计

本设计以STC89C52单片机为核心&#xff0c;其中包含了温湿度检测模块、光照检测模块、PM2.5检测模块、报警电路、LCD显示屏显示电路、按键输入模块和无线传输模块来完成工作。首先&#xff0c;系统可以通过按键输入模块设置当前的时间和报警值&#xff1b;使用检测模块检测当前…

Java中的事件驱动架构(EDA)

引言 在现代软件开发中&#xff0c;事件驱动架构&#xff08;Event-Driven Architecture, EDA&#xff09;越来越受到青睐。EDA是一种软件架构范式&#xff0c;它通过生成、捕获和反应事件来驱动系统行为。在大型分布式系统中&#xff0c;EDA能够帮助我们提高系统的可扩展性、…

Golang 教程6——数组

Golang 教程6——数组 注意&#xff0c;该文档只适合有编程基础的同学&#xff0c;这里的go教程只给出有区别的知识点 1、赋值 1.1 main文件 func main() {//赋值方式1var arr [5] intarr[0] 1arr[1] 2arr[2] 3arr[3] 4arr[4] 5fmt.Println(arr)//赋值方式2var arr2 …

spring security 记住我在web和前后端分离如何使用

一、传统web开发准备工作 如果不懂原理的话&#xff0c;去看上一篇文章&#xff1a;CSDNhttps://mp.csdn.net/mp_blog/creation/editor/141716695 导入需要的依赖包&#xff0c;在传统web页面开发比较简单&#xff0c;我们设置只需要在页面请求参数加上一个remember-me 即可&a…

VS实用的调试技巧

目录 前言 一、是什么bug&#xff1f; 二、什么是调试(Debug) 三、Debug和Release 四、VS调试快捷键 4.1 环境准备 4.2 调试快捷键 五、监视和内存观察 5.1 监视 5.2 内存 六、举例 6.1 例一 6.2 例二 七、数组传参小技巧 八、编程常见错误归类 1. 编译型错误 …

【设计模式-职责链】

定义 职责链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它避免请求的发送者与接收者耦合在一起&#xff0c;让多个对象都有机会处理这个请求。将这些对象连成一条链&#xff0c;并沿着这条链传递请求&#xff0c;直到有对象…

嵌入式Linux:信号分类

目录 1、不可靠信号与可靠信号 1.1、不可靠信号 1.2、可靠信号 2、实时信号和非实时信号 2.1、非实时信号 2.2、实时信号 在Linux系统中&#xff0c;信号可以从两个不同的角度进行分类&#xff1a;一是从可靠性方面&#xff0c;将信号分为可靠信号与不可靠信号&#xff1…

【drools】Rulesengine构建及intelj配置

7.57.0.FinalRulesengineApplication 使用maven构建 intelj 打开文件资源管理器实在是太慢了所以直接把pom 扔到其主页识别为maven项目,自动下载maven包管理器 然后解析依赖: 给maven加一个代理 -DproxyHost=127.0.0.1 -DproxyPort=7890 还是卡主

Python DBUtils介绍

在Python的数据库编程中&#xff0c;高效、安全地管理数据库连接是至关重要的一环。DBUtils是一个Python模块&#xff0c;它提供了一套工具&#xff0c;旨在简化数据库连接的获取、重用和释放过程&#xff0c;从而提高应用程序的性能和可维护性。本文将详细介绍DBUtils的功能、…

深入解析浏览器与Web服务器的通信机制:从URL输入到页面渲染的全过程

浏览器与Web服务器的通信原理 1. 基本交互模式 浏览器向Web服务器发送请求&#xff0c;Web服务器向浏览器返回响应。这种应用程序之间的通信类似于人与人之间的对话,能够彼此响应。 2. 网络通信的基础 网络由多种通信设备组成通信前需确定双方位置进行请求和响应的交互 3.…

Kubernetes共享存储的作用

Kubernetes共享存储的作用 1、共享存储的作用2、实现方式💖The Begin💖点点关注,收藏不迷路💖 在Kubernetes中,对于有状态或需要数据持久化的应用,共享存储至关重要。它确保了即使容器重启或迁移,应用数据也能安全保存,并能在新容器中继续使用。 1、共享存储的作用…

[MySql]保姆级上手教程

介绍 通过数据库管理系统, 编写执行SQL语句, 实现对数据库数据的管理 数据库(DataBase): 储存和管理数据的仓库数据库管理系统(DBMS): 操作和管理数据库的软件SQL语言: 操作关系型数据库的通用语言数据库可以分为关系型数据库和非关系型数据库 相关产品 常见的关系型数据库产…