ThreadPoolExecutor 线程回收时机详解

个人博客

ThreadPoolExecutor 线程回收时机详解 | iwts’s blog

总集

想要完整了解下ThreadPoolExecutor?可以参考:

基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog

Worker-工作线程管理

线程池设计了内部类Worker,主要是用来管理新建的线程,除了监控,核心的方法是:

  1. 执行。
  2. 申请任务。

此外还包括回收等线程监控类型方法。

由于一个工作线程对象,其中有一个具体的线程,那么本质上是不需要加锁的。竞争资源是任务队列,而任务队列由阻塞队列来实现。

可以看Worker的设计:

线程生命周期管理

线程池需要管理线程的生命周期,需要在线程长时间不运行的时候进行回收。

线程池使用一张Hash表去持有线程的引用,这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。

可以看到,workers是HashSet,那么问题来了,线程池有大量的工作线程,频繁创建/清除线程的时候,用线程不安全的HashSet必然是有并发安全问题的。

所以线程池要求在操作workers的时候,都需要获锁,根据该锁对workers进行操作:

也就是说,在工作线程的创建/销毁,都要加上这个锁,例如工作线程的创建:

工作线程的回收

这里比较复杂,慢慢聊。

工作线程自身锁

Worker对象其实本身就是一把锁。这是个细节,Worker本身是实现了AQS的:

这里其实最主要的作用是工作线程的回收。虽然可以通过维护workers来完成对工作线程生命周期的管理,新建线程比较好理解,但是删除线程的时候,工作线程本身就是一种竞争资源了。回收的时候是可能恰好碰到调用的。

这里选择AQS的原因,其实可以看注释,这边简单翻译一部分:

Worker类存在的主要意义就是为了维护线程的中断状态。因为正在执行任务的线程是不应该被中断的。在线程真正开始运行任务之前,为了抑制中断。所以把 Worker 的状态初始化为负数-1。

完全看不懂,这里从其他角度慢慢绕过来解释一下。

线程的中断与回收

解释这个问题,首先看下Worker自身是从哪里调用锁的:

  1. 工作线程处理前后加锁。
  2. 工作线程尝试中断时尝试获锁。

第一个看代码runWorker()

也就是说,当前线程如果在处理,那么本身是给自己加锁的。
第二个看代码interruptIdleWorkers()

这里是不是就有点恍然大悟的意思了。

工作线程本身实现AQS,将自身当作竞争对象。

那么工作线程工作的时候,加锁,锁住自己,那么interruptIdleWorkers()方法在执行的时候,如果能获取锁,就说明一个问题:此时当前线程是没有在工作的。那么就会被中断掉。

为了实现这个功能,就只能选择不可重入锁,所以自己实现了AQS来实现这个特性。具体可以看代码实现。

基本可以推测到,interruptIdleWorkers()这个方法就是回收方法,那么其调用时机是什么?

线程回收时机

一个重要的回收时机-keepAliveTime

这里单独拉出来聊了,比较经典。

八股文一般说:keepAliveTime是线程存活时间,如果当前线程池线程数量大于核心池的时候,如果一个线程超过keepAliveTime没有获取到任务,则会触发线程回收。

这里聊聊相关源码。首先看基础的任务申请:

这里如果设置了超时时间的情况下,请求任务队列是调用的poll()方法,并指定了keepAliveTime。那么这个方法的意思就是,阻塞这么长时间,超过时间后直接返回null。

所以这里就对应到八股文了,如果此时poll()返回空,那么就是说当前队列里什么数据都没有,那么这里其实就是说明:该线程等待了keepAliveTime都没有获取到数据,也就是说这段时间全部是空闲。可以回收了。

而这里只是设置了timedOut标记,留给上层来处理:

这里判定之后返回个null。

直接跳出线程执行run()方法,在finally块中触发线程回收。processWorderExit()方法的底层就是下面的tryTerminate()了,会直接进行回收。

tryTerminate()

核心回收方法,根据其调用可以梳理出正常运行中的回收时机:

  1. 工作线程创建失败时:addWorkerFailed()

  2. runWorker()方法退出时。正常来说runWorker()方法是一个自旋,只有在任务申请失败时才会退出自旋。那么这个时机就是指任务队列已经清空了:

    总体流程为:

  3. shutdown()。可以看到执行了两次。

  4. shutdownNow()。

  5. remove(),移除任务时顺便执行一次。

  6. purge(),todo。

interruptIdleWorkers

一般是针对线程池本身参数进行操作的时候,会触发回收,看其调用方式,可以梳理出来全部的线程回收时机:

  1. shutdown()。
  2. 设置核心池大小的时候,如果当前线程池线程数量大于核心池数量大小,执行一次回收:
  3. 设置允许核心池超时时,执行一次回收:
  4. 设置最大池数量时,如果当前线程池线程数量大于最大池数量,执行一次回收:
  5. 设置线程池线程存活时间时,如果设置变小了,那么执行一次回收:

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

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

相关文章

点餐|外卖订餐小程序|基于微信小程序的外卖订餐系统设计与实现(源码+数据库+文档)

点餐|外卖订餐小程序目录 目录 基于微信小程序的外卖订餐系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户微信端功能模块 2、管理员服务端功能模块 3、商家务端功能模块 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设…

Kafka~高吞吐量设计

Kafka 之所以能够实现高性能和高速度,主要归因于以下几个关键因素: 分布式架构:Kafka 采用分布式架构,可以水平扩展,通过增加服务器节点来处理更多的流量和数据存储。顺序写入磁盘:Kafka 将消息顺序地写入…

重生奇迹MU 正确获取金币的方式

在游戏中,需要消耗大量的金币来购买红药等物品。因此,如何快速赚取金币也成为玩家关注的问题。您知道有哪些方法可以快速地获得金币吗? 一、哪个地图上是最适合打金币的很关键 在选择打钱的地方时,不能盲目行动,需要…

【C++开发必备工具】Dependency Walker与Dependencies

Dependency Walker 与 Dependencies 1. Dependency Walker1.1 功能特点1.2 使用方法1.3 注意事项 2. Dependencies2.1 功能特点2.2 使用方法2.3 注意事项 3. 总结 1. Dependency Walker Dependency Walker 是一个免费软件工具,用于查看 Windows 应用程序的模块&…

数据库优化方式

优化MySQL数据库性能可以通过多种方式实现,这些方式包括但不限于: 索引优化: 确保经常用于查询的列上创建索引,以加快查询速度。避免创建过多的索引,因为它们会增加写操作的成本。 查询优化: 编写高效的SQ…

[数据集][目标检测]婴儿状态睡觉哭泣检测数据集VOC+YOLO格式7109张3类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):7109 标注数量(xml文件个数):7109 标注数量(txt文件个数):7109 标注…

C++语法基础:引用

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 引用是C里的概念,和C语言里的"指针常量"是类似的.在C里用得还挺多的,书中明确说明了类对象做参数时,传入类对象的引用.在<<C Prime Plus>> 6th Edition第274页有使用推荐 .用引用来回顾指针…

Cmake--学习笔记

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

leetCode.96. 不同的二叉搜索树

leetCode.96. 不同的二叉搜索树 题目思路 代码 // 方法一&#xff1a;直接用卡特兰数就行 // 方法二&#xff1a;递归方法 class Solution { public:int numTrees(int n) {// 这里把 i当成整个结点&#xff0c;j当成左子树最左侧结点,并一次当根节点尝试// f[ i ] f[ j - 1…

PostgreSQL的系统视图pg_statio_all_indexes

PostgreSQL的系统视图pg_statio_all_indexes 在 PostgreSQL 数据库中&#xff0c;pg_statio_all_indexes 视图提供了有关所有索引的 I/O 活动的统计信息。这些统计信息对于了解索引的使用情况和性能调优非常有帮助。 pg_statio_all_indexes 视图的结构 以下是 pg_statio_all…

基于Java的会员制医疗预约服务管理信息系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术ssm框架&#xff0c;结合JSPM工作流引擎 工具&#xff1a;IDEA/Eclipse、Navicat、Maven …

10大内网安全管理系统!企业内网安全必备系统

内网安全管理系统对于维护企业网络安全至关重要&#xff0c;它们帮助监控、管理内部网络资源&#xff0c;防止数据泄露和安全威胁。以下是十款知名的内网安全管理系统。 1. 安企神终端安全管理系统 详细介绍&#xff1a; 安企神是针对企业内网安全需求设计的一款综合管理系统&…

记一次windows 资源管理器 explorer.exe无限重启(桌面2秒一直刷新)问题的排查

一. 现象 进入锁屏界面一切正常&#xff0c;输入密码进入桌面后&#xff0c;开始无限刷新&#xff0c;无法正常操作&#xff0c;任务栏也不显示。CtrlAltDel 打开任务管理器&#xff0c;可见windows资源管理器进程时而出现&#xff0c;时而消失。进入安全模式一切正常。window…

kubernetes中使用Helm搭建Redis集群

1. 环境要求 首先需要有kubernetes集群环境&#xff0c;搭建kubernetes集群可以使用kubeSphere、kubespray等工具安装集群。其次要安装helm&#xff0c;并且添加了可用的Chart仓库 2. 确认合适的Chart包 helm官网上搜索redis 找到Star数多的Chart包&#xff0c;一类是redis包…

乐鑫 Matter 技术体验日|快速落地 Matter 产品,引领智能家居生态新发展

随着 Matter 协议的推广和普及&#xff0c;智能家居行业正迎来新的发展机遇&#xff0c;众多厂商纷纷投身于 Matter 产品的研发与验证。然而&#xff0c;开发者普遍面临技术门槛高、认证流程繁琐、生产管理复杂等诸多挑战。 乐鑫信息科技 (688018.SH) 凭借深厚的研发实力与行…

Gin框架基础

1、一个简单的Gin示例 下载并安装Gin: go get -u github.com/gin-gonic/gin1.1 一个简单的例子 package mainimport ("net/http""github.com/gin-gonic/gin" )func main() {// 创建一个默认的路由引擎r : gin.Default()// 当客户端以GET方式访问 /hello…

常见Web认证方式对比

认证是一个在用户或者设备在访问一个受限的系统时&#xff0c;鉴定用户凭据的过程&#xff0c;即确认“你是谁”的问题。最常见的认证用户的方式是通过用户名和密码的形式进行校验&#xff0c;目前存在多种校验方式&#xff0c;本文将对其进行一个简单的对比&#xff0c;使得大…

flutter项目中不能使用在ios样式组件下的组件有哪些

InkWell 未完待续&#xff0c;也请大家补充&#xff0c;评论区见

TensorFlow 的原理与使用

文章目录 TensorFlow 的基本原理1. 计算图&#xff08;Computation Graph&#xff09;2. 张量&#xff08;Tensor&#xff09;3. 会话&#xff08;Session&#xff09;4. 自动微分&#xff08;Automatic Differentiation&#xff09; TensorFlow 的使用安装 TensorFlow基本使用…

DDR3自刷新问题

DDR3 内存中的自刷新和手动刷新是两种不同的刷新机制&#xff0c;它们在目的、操作方式和使用场景上有所不同。让我们来比较这两种刷新方式&#xff1a; 自刷新&#xff09;&#xff1a; 目的&#xff1a;在系统低功耗模式下保持数据完整性。操作&#xff1a;由 DRAM 内部的电路…