ThreadPoolExecutor应用源码剖析(三)

3.3.5 ThreadPoolExecutor的Worker工作线程

Worker对象主要包含了两个内容
● 工作线程要执行任务
● 工作线程可能会被中断,控制中断

// Worker继承了AQS,目的就是为了控制工作线程的中断。
// Worker实现了Runnable,内部的Thread对象,在执行start时,必然要执行Worker中断额一些操作
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
// =======================Worker管理任务================================
// 线程工厂构建的线程
final Thread thread;// 当前Worker要执行的任务
Runnable firstTask;
// 记录当前工作线程处理了多少个任务。
volatile long completedTasks;
// 有参构造
Worker(Runnable firstTask) {
// 将State设置为-1,代表当前不允许中断线程
setState(-1);
// 任务赋值
this.firstTask = firstTask;
// 基于线程工作构建Thread,并且传入的Runnable是Worker
this.thread = getThreadFactory().newThread(this);
}
// 当thread执行start方法时,调用的是Worker的run方法,
public void run() {
// 任务执行时,执行的是runWorker方法
runWorker(this);
}
// =======================Worker管理中断================================
// 当前方法是中断工作线程时,执行的方法
void interruptIfStarted() {Thread t;
// 只有Worker中的state >= 0的时候,可以中断工作线程
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
// 如果状态正常,并且线程未中断,这边就中断线程
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
}

3.3.6 ThreadPoolExecutor的runWorker方法

runWorker就是让工作线程拿到任务去执行即可。
并且在内部也处理了在工作线程正常结束和异常结束时的处理方案

// 工作线程启动后执行的任务。
final void runWorker(Worker w) {
// 拿到当前线程
Thread wt = Thread.currentThread();
// 从worker对象中拿到任务
Runnable task = w.firstTask;
// 将Worker中的firstTask置位空
w.firstTask = null;
// 将Worker中的state置位0,代表当前线程可以中断的
w.unlock(); // allow interrupts
// 判断工作线程是否是异常结束,默认就是异常结束
boolean completedAbruptly = true;
try {
// 获取任务// 直接拿到第一个任务去执行
// 如果第一个任务为null,去阻塞队列中获取任务
while (task != null || (task = getTask()) != null) {
// 执行了Worker的lock方法,当前在lock时,shutdown操作不能中断当前线程,因为当前线程正在处理任务
w.lock();
// 比较ctl >= STOP,如果满足找个状态,说明线程池已经到了STOP状态甚至已经要凉凉了
// 线程池到STOP状态,并且当前线程还没有中断,确保线程是中断的,进到if内部执行中断方法
// if(runStateAtLeast(ctl.get(), STOP) && !wt.isInterrupted()) {中断线程}
// 如果线程池状态不是STOP,确保线程不是中断的。
// 如果发现线程中断标记位是true了,再次查看线程池状态是大于STOP了,再次中断线程
// 这里其实就是做了一个事情,如果线程池状态 >= STOP,确保线程中断了。
if (
(
runStateAtLeast(ctl.get(), STOP) ||
( Thread.interrupted() && runStateAtLeast(ctl.get(), STOP) )
)
&& !wt.isInterrupted())
wt.interrupt();
try {
// 勾子函数在线程池中没有做任何的实现,如果需要在线程池执行任务前后做一些额外的处理,可以重写勾子函数
// 前置勾子函数
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 执行任务。
task.run();
} catch (RuntimeException x) {thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
// 前后置勾子函数
afterExecute(task, thrown);
}
} finally {
// 任务执行完,丢掉任务
task = null;
// 当前工作线程处理的任务数+1
w.completedTasks++;
// 执行unlock方法,此时shutdown方法才可以中断当前线程
w.unlock();
}
}
// 如果while循环结束,正常走到这,说明是正常结束
// 正常结束的话,在getTask中就会做一个额外的处理,将ctl - 1,代表工作线程没一个。
completedAbruptly = false;
} finally {
// 考虑干掉工作线程
processWorkerExit(w, completedAbruptly);
}
}
// 工作线程结束前,要执行当前方法
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果是异常结束
if (completedAbruptly)
// 将ctl - 1,扣掉一个工作线程
decrementWorkerCount();
// 操作Worker,为了线程安全,加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 当前工作线程处理的任务个数累加到线程池处理任务的个数属性中
completedTaskCount += w.completedTasks;
// 将工作线程从hashSet中移除
workers.remove(w);
} finally {
// 释放锁
mainLock.unlock();
}
// 只要工作线程凉了,查看是不是线程池状态改变了。
tryTerminate();
// 获取ctl
int c = ctl.get();
// 判断线程池状态,当前线程池要么是RUNNING,要么是SHUTDOWN
if (runStateLessThan(c, STOP)) {
// 如果正常结束工作线程if (!completedAbruptly) {
// 如果核心线程允许超时,min = 0,否则就是核心线程个数
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果min == 0,可能会出现没有工作线程,并且阻塞队列有任务没有线程处理
if (min == 0 && ! workQueue.isEmpty())
// 至少要有一个工作线程处理阻塞队列任务
min = 1;
// 如果工作线程个数 大于等于1,不怕没线程处理,正常return
if (workerCountOf(c) >= min)
return;
}
// 异常结束,为了避免出现问题,添加一个空任务的非核心线程来填补上刚刚异常结束的工作线程
addWorker(null, false);
}
}

3.3.7 ThreadPoolExecutor的getTask方法

工作线程在去阻塞队列获取任务前,要先查看线程池状态
如果状态没问题,去阻塞队列take或者是poll任务
第二个循环时,不但要判断线程池状态,还要判断当前工作线程是否可以被干掉

// 当前方法就在阻塞队列中获取任务
// 前面半部分是判断当前工作线程是否可以返回null,结束。
// 后半部分就是从阻塞队列中拿任务
private Runnable getTask() {
// timeOut默认值是false。
boolean timedOut = false;
// 死循环
for (;;) {
// 拿到ctl
int c = ctl.get();
// 拿到线程池的状态
int rs = runStateOf(c);
// 如果线程池状态是STOP,没有必要处理阻塞队列任务,直接返回null
// 如果线程池状态是SHUTDOWN,并且阻塞队列是空的,直接返回null
if (rs >= SHUTDOWN &&
(rs >= STOP || workQueue.isEmpty())) {
// 如果可以返回null,先扣减工作线程个数
decrementWorkerCount();
// 返回null,结束runWorker的while循环
return null;
}
// 基于ctl拿到工作线程个数
int wc = workerCountOf(c);
// 核心线程允许超时,timed为true
// 工作线程个数大于核心线程数,timed为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;if (
// 如果工作线程个数,大于最大线程数。(一般情况不会满足),把他看成false
// 第二个判断代表,只要工作线程数小于等于核心线程数,必然为false
// 即便工作线程个数大于核心线程数了,此时第一次循环也不会为true,因为timedOut默认值是false
// 考虑第二次循环了,因为循环内部必然有修改timeOut的位置
(wc > maximumPoolSize || (timed && timedOut))
&&
// 要么工作线程还有,要么阻塞队列为空,并且满足上述条件后,工作线程才会走到if内部,结束工作线程
(wc > 1 || workQueue.isEmpty())
) {
// 第二次循环才有可能到这。
// 正常结束,工作线程 - 1,因为是CAS操作,如果失败了,重新走for循环
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
// 工作线程从阻塞队列拿任务
try {
// 如果是核心线程,timed是false,如果是非核心线程,timed就是true
Runnable r = timed ?
// 如果是非核心,走poll方法,拿任务,等待一会
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 如果是核心,走take方法,死等。
workQueue.take();
// 从阻塞队列拿到的任务不为null,这边就正常返回任务,去执行if (r != null)
return r;
// 说明当前线程没拿到任务,将timeOut设置为true,在上面就可以返回null退出了。
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}

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

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

相关文章

QT4和 QT5 槽函数连接的区别

正常连接方式 //QT4官方用列QLabel *label new QLabel;QScrollBar *scrollBar new QScrollBar;QObject::connect(scrollBar, SIGNAL(valueChanged(int)),label, SLOT(setNum(int)));//QT5官方用列QLabel *label new QLabel;QLineEdit *lineEdit new QLineEdit;QObject::c…

STK Components 二次开发-飞行器

1.创建飞机 参数帮助文档 var poitList GetTracksData(); var waypointPropagator new WaypointPropagator(m_earth, poitList); var locationPoint waypointPropagator.CreatePoint();m_aircraft new Platform {Name "MH730",LocationPoint locationPoint,Or…

首次部署Linux系统的经历

我是一名电子信息工程专业的学生,有次在图书馆上自习的时候无意间看到其他同学的电脑屏幕,黑色的屏幕上显示着一行一行的代码,勾起了我无限的好奇,经过询问得知他是用的Linux操作系统,是和Windows完全不同的系统&#…

JDBC操作

本博客主要是介绍JDBC操作,即通过编译器操纵数据库中的数据。接下来以插入操作简单介绍该操作。 首先在创建的项目中,添加下列jar包(点击可加载下载页面)。 mysql-connector-java-5.1.49.jar 然后编写JDBC代码 public class JDB…

vue3 中使用 sse 最佳实践,封装工具

工具 // 接受参数 export interface SSEChatParams {url: string,// sse 连接onmessage: (event: MessageEvent) > void,// 处理消息的函数onopen: () > void,// 建立连接触发的事件finallyHandler: () > void,// 相当于 try_finally 中的 finally 部分,不…

机器学习(2)回归

0.前提 上一期,我们简单的介绍了一些有关机器学习的内容。学习机器学习的最终目的是为了服务我未来的毕设选择之一——智能小车,所以其实大家完全可以根据自己的需求来学习这门课,我做完另一辆小车后打算花点时间去进行一次徒步行&#xff0…

C++现代模板元编程

序 个人发现很多国外的大佬的演讲或者文章都很不错,但是鲜有人来进行分享,届后本人会时不时拿一些看起来很好的东西来给大家分享,主要也是搬运,不过也省去了大家去读英文的麻烦,同时文章中也会参杂着一些自己的见解。…

游戏mod制作--引擎与解包

摘要 游戏mod的制作过程第一步就是需要将原始的游戏工程文件进行解包,得到相应的资源文件(贴图,音频,事件,模型甚至源代码等),这个时候下一步就是需要将解包出来的文件进行分类索引&#xff0c…

服务器内存使用率高的原因及解决方法_Maizyun

服务器内存使用率高的原因及解决方法 在服务器运行过程中,内存使用率过高可能会引发一系列问题,如性能下降、应用程序崩溃等。本文将深入探讨服务器内存使用率高的原因,并提出相应的解决方法。 一、内存使用率高的原因 应用程序缺陷&#…

20:kotlin 类和对象 --泛型(Generics)

类可以有类型参数 class Box<T>(t: T) {var value t }要创建类实例&#xff0c;需提供类型参数 val box: Box<Int> Box<Int>(1)如果类型可以被推断出来&#xff0c;可以省略 val box Box(1)通配符 在JAVA泛型中有通配符?、? extends E、? super E&…

25. K 个一组翻转链表

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

自媒体原创改写工具,自媒体首发改写软件

自媒体平台已成为许多创作者表达观点、分享知识和积累影响力的关键渠道。创作是需要技巧和经验的。本文将分享一些自媒体文章改写技巧&#xff0c;并推荐一系列优秀的自媒体文章改写工具&#xff0c;帮助您提升创作效率&#xff0c;创作出更优秀的文章。 自媒体文章改写技巧 …

Backend - Django makemigrations

目录 一、迁移命令 &#xff08;一&#xff09;前提 &#xff08;二&#xff09;生成迁移文件 &#xff08;三&#xff09;执行迁移 二、迁移问题 1. Error&#xff1a;No changes detected 2. Error&#xff1a;You are trying to add a non-nullable field XXX to XXX…

[读论文]BK-SDM: A Lightweight, Fast, and Cheap Version of Stable Diffusion

github: GitHub - Nota-NetsPresso/BK-SDM: A Compressed Stable Diffusion for Efficient Text-to-Image Generation [ICCV23 Demo] [ICML23 Workshop] ICML 2023 Workshop on ES-FoMo 简化方式 蒸馏方式&#xff08;训练Task蒸馏outKD-FeatKD&#xff09; 训练数据集 评测指标…

在intelliJ spring boot gradle插件3.2.0中未找到匹配的变量

我正在尝试使用spring启动Gradle插件的版本3.2.0。这是我的build.gradle文件&#xff1a; plugins {id javaid org.springframework.boot version 3.2.0id io.spring.dependency-management version 1.1.4 }group com.yaxin version 0.0.1-SNAPSHOTjava {sourceCompatibilit…

GPIO的使用--时钟使能含义--代码封装

目录 一、时钟使能的含义 1.为什么要时钟使能&#xff1f; 2.什么是时钟使能&#xff1f; 3.GPIO的使能信号&#xff1f; 二、代码封装 1.封装前完整代码 2.封装结构 封装后代码 led.c led.h key.c key.h main.c 一、时钟使能的含义 1.为什么要时钟使能&#xff1f…

Python开发运维:Python 3.8 常用标准库

目录 一、理论 1.Python3.8 标准库 2.常用标准库 二、问题 1.Python 正则表达式如何实现 一、理论 1.Python3.8 标准库 &#xff08;1&#xff09;官网 Python 标准库 — Python 3.8.17 文档 &#xff08;2&#xff09;其他版本下拉列表查询 2.常用标准库 &#xff0…

MySQL笔记-第01章_数据库概述

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第01章_数据库概述1. 为什么要使用数据库2. 数据库与数据库管理系统2.1 数据库的相关概念2.2 数据库与数据库管理系统的关系2.3 常见的数据库…

JVM参数配置推荐

JVM配置建议 参数 备注/参数释义 规范 JVM GC方法 ParallelGC&#xff1a;1.8默认&#xff0c;高吞吐量&#xff0c;响应时间不敏感 CMS&#xff1a;响应优先&#xff0c;堆内存8G以下优先选择 G1&#xff1a;响应优先&#xff0c;堆内存8G及以上选择 C端应用&#xff1a;…

微积分中值定理的双存在值证明问题@寻找辅助函数证明中值问题

文章目录 abstract微积分中值定理的双存在值证明题举例综合使用积分中值定理和微分中值定理例 双中值问题构造辅助函数法综合小结 abstract 微积分中值定理的双存在值证明问题寻找辅助函数证明中值问题 微积分中值定理的双存在值证明题举例 综合使用积分中值定理和微分中值定…