线程池详细解析(二)

article/2025/7/1 17:41:09

本章我们将继续讲述线程池的源码解析给,上一章我们了解了一下Worker内部类这个用作包装线程池的工作线程的内部类。本章我们看看他的核心方法

        Worker(Runnable var2) {this.setState(-1);this.firstTask = var2;this.thread = ThreadPoolExecutor.this.getThreadFactory().newThread(this);}public void run() {ThreadPoolExecutor.this.runWorker(this);}

首先构造方法中将自己的状态值设置为-1表示不可中断(这里的状态是AQS的state状态值),同时设置任务将属性指向任务,并且初始化一个线程这里的线程初始化是通过线程池的线程工厂创建的。

随后Worker最核心的方法则是run方法了,它表示线程执行任务的业务逻辑是怎么执行的。

runWorker

我们可以看出它本质上是调用线程池的runWorker方法来执行的,下面我们看看runWorker方法是如何执行的

    final void runWorker(Worker var1) {Thread var2 = Thread.currentThread();Runnable var3 = var1.firstTask;var1.firstTask = null;var1.unlock();boolean var4 = true;try {while(var3 != null || (var3 = this.getTask()) != null) {var1.lock();if ((runStateAtLeast(this.ctl.get(), 536870912) || Thread.interrupted() && runStateAtLeast(this.ctl.get(), 536870912)) && !var2.isInterrupted()) {var2.interrupt();}try {this.beforeExecute(var2, var3);Object var5 = null;try {var3.run();} catch (RuntimeException var28) {var5 = var28;throw var28;} catch (Error var29) {var5 = var29;throw var29;} catch (Throwable var30) {var5 = var30;throw new Error(var30);} finally {this.afterExecute(var3, (Throwable)var5);}} finally {var3 = null;++var1.completedTasks;var1.unlock();}}var4 = false;} finally {this.processWorkerExit(var1, var4);}}

先整体的过一遍这个方法,首先获取当前Worker的一些属性,然后调用Worker的unlock将其置为可中断状态,随后开始通过while循环来不断的获取任务进行执行,首先看是否Worker本身携带任务如果没有则调用getTask方法从线程池的任务队列中获取任务执行。当获取到任务的时候就将当前Worker上锁也就是state置为0表示忙碌状态,再次判断当前线程池的状态以及线程状态,随后根据状态选择是否重置线程的中断标志位。在执行当前线程任务的时候会调用beforeExecute前置方法和afterExecute后置方法,执行完毕之后则会将当前的任务数量+1随后进行解锁处理。最后如果跳出循环之后表明当前线程由于某种原因而不需要了则会调用processWorkerExit对其进行清除处理。

接下来我们着重分析一下getTask方法和processWorkerExit方法

getTask

    private Runnable getTask() {boolean var1 = false;while(true) {int var2 = this.ctl.get();int var3 = runStateOf(var2);if (var3 >= 0 && (var3 >= 536870912 || this.workQueue.isEmpty())) {this.decrementWorkerCount();return null;}int var4 = workerCountOf(var2);boolean var5 = this.allowCoreThreadTimeOut || var4 > this.corePoolSize;if (var4 <= this.maximumPoolSize && (!var5 || !var1) || var4 <= 1 && !this.workQueue.isEmpty()) {try {Runnable var6 = var5 ? (Runnable)this.workQueue.poll(this.keepAliveTime, TimeUnit.NANOSECONDS) : (Runnable)this.workQueue.take();if (var6 != null) {return var6;}var1 = true;} catch (InterruptedException var7) {var1 = false;}} else if (this.compareAndDecrementWorkerCount(var2)) {return null;}}}

其中第一个变量的var1表示的是当前线程是否是因为超时,在while循环中,进行状态判断如果状态不符合要求则直接返回结果同时将线程数目-1,如果符合要求则将其得到线程数量进行判断,如果当前线程大于核心数量则将var5 设置为true表示是否计时

线程池是根据线程数量来对线程数量进行控制的,线程如果一个线程刚开始是核心线程之后如果线程数量超过核心数量之后还是有可能会被删除的。线程并不是有着固定的身份的随时都有可能被清除的

随后进判断几个属性的状态位判断,如果符合要求就从队列中获取任务,不过这里获取的方式不同,如果是有时间限制则会调用阻塞队列的offer方法在规定时间内获取任务,否则则会一直阻塞直到有任务来到。

当获取到任务后则直接返回任务

如果没有获取到任务则会将超时标志为设置为true,随后继续循环。

下面解析一下var4 <= this.maximumPoolSize && (!var5 || !var1) || var4 <= 1 && !this.workQueue.isEmpty()的作用

这个判断分为两个部分,首先第一部分则是

线程数是否超限或者是否需退出
  • var4 <= maximumPoolSize:当前线程数未超过最大限制,允许继续获取任务。
  • !var5 || !var1
    • !var5(即 !timed):线程不使用超时策略(核心线程且不允许超时)。
    • !var1(即 !timedOut):线程未超时(上次获取任务成功或未超时)。

意义:线程数合规且未超时,或无需超时(核心线程),则继续尝试获取任务。

特殊保护逻辑
  • var4 <= 1:当前线程是线程池中仅剩的 1 个线程。
  • !workQueue.isEmpty():任务队列中仍有未处理的任务。

意义:若只剩 1 个线程且队列非空,该线程必须继续运行,防止所有线程退出导致任务饿死。

也就是说如果当前线程已经退出后只要不需要特殊保护都会进入到下一个判断

else if (this.compareAndDecrementWorkerCount(var2))也就是CAS扣减线程

当扣减完成之后则直接退出。

当退出后返回runWorker方法

try {
while(var3 != null || (var3 = this.getTask()) != null){
。。。。。
}
var4 = false;
} finally {this.processWorkerExit(var1, var4);
}

则会跳出while循环,最后进入到processWorkerExit方法中去。

processWorkerExit:

    private void processWorkerExit(Worker var1, boolean var2) {if (var2) {this.decrementWorkerCount();}ReentrantLock var3 = this.mainLock;var3.lock();try {this.completedTaskCount += var1.completedTasks;this.workers.remove(var1);} finally {var3.unlock();}this.tryTerminate();int var4 = this.ctl.get();if (runStateLessThan(var4, 536870912)) {if (!completedAbruptly) { // 线程是正常退出(非异常)int var5 = allowCoreThreadTimeOut ? 0 : corePoolSize;if (var5 == 0 && !workQueue.isEmpty())var5 = 1;// 若允许核心线程超时且队列为空,至少保留1个线程if  (workerCountOf(var4) >= var5 )return; // 已有足够线程,无需补充}this.addWorker((Runnable)null, false);}}

第二个参数则是用来表示是否为异常退出的,首先判断是否为异常退出是则回滚任务执行数量。

随后获取全局锁进行上锁,累计线程池已完成的任务总数和从线程集合中移除当前worker,之后进行解锁

随后调用tryTerminate方法检查线程池是否满足完全终止的条件(如已调用 shutdown() 且队列为空),若满足则完成终止流程。

随后进状态判断是否为run或者shutdown状态

  • RUNNING:线程池正常运行,可能需要补充线程。
  • SHUTDOWN:已调用 shutdown(),不再接受新任务,但会处理完队列中的任务,可能需要补充线程。

之后如果是非异常中断则进行线程数量判断查看是否需要再次创建线程,如果不需要则直接返回。

如果需要或者异常中断的线程那么就会再新增一个新的线程调用addWorker方法。


http://www.hkcw.cn/article/ArvDBBQWDf.shtml

相关文章

docker运行程序Killed异常排查

问题描述 我最近开发了一个C 多线程程序&#xff0c;测试没有问题&#xff0c;封装docker测试也没有问题&#xff0c;然后提交给客户了&#xff0c;然后在他那边测试有问题&#xff0c;不定时、不定位置异常中断&#xff0c;以前一直认为只要封装了docker就万事大吉&#xff0…

Linux--进程概念

1.基本概念与基本操作 • 课本概念&#xff1a;程序的⼀个执⾏实例&#xff0c;正在执⾏的程序等 • 内核观点&#xff1a;担当分配系统资源&#xff08;CPU时间&#xff0c;内存&#xff09;的实体。 2 描述进程-PCB 基本概念 • 进程信息被放在⼀个叫做进程控制块的数据…

铁电液晶破局 VR/AR:10000PPI 重构元宇宙显示体验

一、VR/AR 沉浸感困境&#xff1a;传统显示技术的天花板在哪&#xff1f; &#xff08;一&#xff09;纱窗效应与眩晕感&#xff1a;近眼显示的双重枷锁 当用户戴上 VR 头显&#xff0c;眼前像素网格形成的 “纱窗效应” 瞬间打破沉浸感。传统液晶 500-600PPI 的像素密度&…

edge进行重置设置之后,网页无法访问(显示连接不是专用连接)

问题&#xff1a; edge进行重置设置之后&#xff0c;网页无法访问&#xff08;显示连接不是专用连接&#xff09;&#xff0c;如下图&#xff1a; 解决方法&#xff1a; 调整键盘为英文输入状态&#xff0c;刷新一下页面&#xff0c;鼠标点击页面任意位置&#xff08;不要点击到…

sql注入补充——get注入

Sql注入 Mysql中的information_schema库 用于存储数据库元信息&#xff0c;包含了数据库、表、列、索引等结构化信息。 特点&#xff1a; 只读性 标准化&#xff1a;它是sql标准的一部分&#xff0c;适用于多种数据库系统 动态生成&#xff1a;数据是动态生成的&#xff…

eBay关键词搜索API开发指南

一、接口概述 eBay的Finding API提供findItemsByKeywords方法&#xff0c;支持通过关键词检索商品列表。该接口采用REST架构&#xff0c;返回标准JSON/XML格式数据&#xff0c;日均调用限额5000次&#xff08;生产环境需申请提升配额&#xff09;。 二、核心参数说明 必需参…

<6>, 界面优化

目录 一&#xff0c;QSS 1&#xff0c;背景介绍 2&#xff0c;基本语法 3&#xff0c;设置方式 &#xff08;1&#xff09;指定控件样式设置 &#xff08;2&#xff09;全局样式设置 &#xff08;3&#xff09;从文件加载样式表 &#xff08;4&#xff09;使用 Qt Desig…

截图工具 Snipaste V2.10.7(2025.06.2更新)

—————【下 载 地 址】——————— 【​本章下载一】&#xff1a;https://pan.xunlei.com/s/VORklK9hcuoI6n_qgx25jSq2A1?pwde7bi# 【​本章下载二】&#xff1a;https://pan.quark.cn/s/7c62f8f86735 【百款黑科技】&#xff1a;https://ucnygalh6wle.feishu.cn/wiki/…

Docker安装Redis集群(3主3从+动态扩容、缩容)保姆级教程含踩坑及安装中遇到的问题解决

前言 部署集群前&#xff0c;我们需要先掌握Redis分布式存储的核心算法。了解这些算法能帮助我们在实际工作中做出合理选择&#xff0c;同时清晰认识各方案的优缺点。 一、分布式存储算法 我们通过一道大厂面试题来进行阐述。 如下&#xff1a;1-2亿条数据需要缓存&#xff…

Altium Disigner(16.1)学习-元器件封装

一、元器件封装 封装就是给画的原理图所有的器件的外形描述出来&#xff08;几个引脚啦、引脚之间的长度啦、宽度啦&#xff09;&#xff0c;一定要精确。否则等到真正元器件焊在板子上的时候&#xff0c;会发现根本焊不上去&#xff0c;可能就是焊盘的位置不够精确。 可以点击…

初识CSS3

1. 什么是CSS <style>标签 行内样式 内部样式表 外部样式表⭐ CSS样式优先级⭐ 2. CSS3基本选择器 标签选择器 类选择器 ID选择器 基本选择器的特点⭐ 基本选择器的优先级⭐ 3. CSS3高级选择器-层次选择器 后代选择器 子选择器 相邻兄弟选择器 通用兄弟选择器 4. CSS3高级…

Python训练营---Day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 进阶&#xff1a;并拆分成多个文件 数据集来源水母图像数据集 --- Jellyfish Image Dataset&#xff0c;对水母图片进行分类&#xff0c;共6个类别。 数据集文件…

NLP学习路线图(十八):Word2Vec (CBOW Skip-gram)

自然语言处理&#xff08;NLP&#xff09;的核心挑战在于让机器“理解”人类语言。传统方法依赖独热编码&#xff08;One-hot Encoding&#xff09; 表示单词&#xff0c;但它存在严重缺陷&#xff1a;每个单词被视为孤立的符号&#xff0c;无法捕捉词义关联&#xff08;如“国…

win32相关(事件)

事件 什么是事件&#xff1f; 事件是指系统或应用程序中发生的特定动作或状态变化&#xff0c;这些动作或变化可以被程序检测并响应。Windows 采用事件驱动的编程模型&#xff0c;应用程序主要通过处理各种事件来与用户交互 我们来看一下事件的函数 HANDLE CreateEventW(LPSE…

VisionPro项目记录3 —— 圆心距

简介&#xff1a;本项目实现基于Cognex视觉系统的两圆心对位功能&#xff0c;使用一个圆作为基准&#xff0c;另一个圆进行补偿&#xff0c;输出偏移值给PLC或机械手。 系统采用CogFindCircleTool定位两圆心坐标&#xff0c;通过脚本计算圆心距并乘以缩放系数kValue&#xff0…

面向连接的运输:TCP

目录 TCP连接 TCP报文段结构 往返时间估计与超时 可靠数据传输 回退N步or超时重传 超时间隔加倍 快速重传 流量控制 TCP连接管理 三次握手 1. 客户端 → 服务器&#xff1a;SYN 包 2. 服务器 → 客户端&#xff1a;SYNACK 包 3. 客户端 → 服务器&#xff1a;AC…

使用Python进行函数作画

前言 因为之前通过deepseek绘制一下卡通的人物根本就不像&#xff0c;又想起来之前又大佬通过函数绘制了一些图像&#xff0c;想着能不能用Python来实现&#xff0c;结果发现可以&#xff0c;不过一些细节还是需要自己调整&#xff0c;deepseek整体的框架是没有问题&#xff0…

C#项目07-二维数组的随机创建

实现需求 创建二维数组&#xff0c;数组的列和宽为随机&#xff0c;数组内的数也是随机 知识点 1、Random类 Public Random rd new Random(); int Num_Int rd.Next(1, 100);2、数组上下限。 //定义数组 int[] G_Array new int[1,2,3,4];//一维数组 int[,] G_Array_T …

java Semaphore‌

Java Semaphore 用于控制同时访问特定资源的线程数量&#xff0c;通过管理一组“许可”&#xff08;permits&#xff09;实现并发限制。 模拟6人上厕所&#xff0c;但只有两个坑位&#xff0c;测试代码&#xff1a; import java.util.concurrent.Semaphore;// 假设厕所只有俩…

代码随想录算法训练营第60期第五十五天打卡

大家好&#xff0c;我们今天继续我们图论的部分&#xff0c;其实我们昨天是主要讲解了深搜与广搜的理论基础&#xff0c;我们大体上了解了两种算法的差异与适用情景&#xff0c;今天我们就继续我们的图论的章节&#xff0c;以后几天的题目是图论中比较有名的问题叫做岛屿问题&a…