Java并发编程-线程池(三)
文章目录
- 线程池实现原理
- addWorker(Runnable firstTask, boolean core)
- 1. 状态检查:校验线程池是否允许添加线程
- 2. 工作线程数调整:CAS保证并发安全
- 3. 初始化变量
- 4. 创建 `Worker` 对象并获取线程
- 5. 加锁保证线程安全
- 6. 启动工作线程
- 7. 异常处理
- 核心作用
线程池实现原理
接下来,我们进入 addWork 方法, 在创建线程前会获取全局锁:
addWorker(Runnable firstTask, boolean core)
/* Set containing all worker threads in pool. Accessed only when* holding mainLock.*/
private final HashSet<Worker> workers = new HashSet<Worker>();
private boolean addWorker(Runnable firstTask, boolean core) {retry:for (;;) {int c = ctl.get();int rs = runStateOf(c);if (rs >= SHUTDOWN && ! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()))/ 若状态为STOP/TIDYING/TERMINATED(rs >= SHUTDOWN),直接拒绝新增线程。例外情况:当状态为SHUTDOWN且满足以下条件时允许添加 非核心线程 处理残留任务:1. firstTask == null(线程不携带新任务)2. workQueue非空(队列中仍有待处理任务)*/return false;for (;;) {int wc = workerCountOf(c); // 工作线程数(低29位)if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))// 容量校验return false;if (compareAndIncrementWorkerCount(c))// CAS递增workerCountbreak retry; // 成功则退出整个retry循环c = ctl.get(); // 重新读取当前ctlif (runStateOf(c) != rs)continue retry; // 状态变化则重试外层循环// 若仅workerCount变化,继续内层循环重试CAS}}boolean workerStarted = false; //标记工作线程是否成功启动boolean workerAdded = false;//标记线程是否被成功添加到线程池Worker w = null;try {final ReentrantLock mainLock = this.mainLock;w = new Worker(firstTask);//通过线程工厂创建Worker对象(后面讲)final Thread t = w.thread;//获取其绑定的线程if (t != null) {mainLock.lock(); // 获取全局锁try {//重新检查线程池状态int c = ctl.get();int rs = runStateOf(c);if (rs < SHUTDOWN || //线程池处于RUNNING状态时,允许添加新线程(rs == SHUTDOWN && firstTask == null)) {//SHUTDOWN状态下仅允许添加无初始任务的线程(处理队列中的剩余任务)if (t.isAlive()) // 防止重复启动throw new IllegalThreadStateException();workers.add(w);int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;}} finally {mainLock.unlock();}if (workerAdded) {//若成功添加Worker,则启动其线程(真正开始消费任务)t.start();workerStarted = true;}}} finally {if (! workerStarted)//失败回滚:若未成功启动(如线程池已关闭),调用addWorkerFailed回滚资源addWorkerFailed(w);}return workerStarted;
}
private void addWorkerFailed(Worker w) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (w != null)workers.remove(w); //移除WorkerdecrementWorkerCount();//减少计数tryTerminate(); //尝试终止线程池} finally {mainLock.unlock();}
}
1. 状态检查:校验线程池是否允许添加线程
int c = ctl.get();
int rs = runStateOf(c); // 运行状态(高3位)
if (rs >= SHUTDOWN &&!(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))return false;
-
关键条件:
-
若状态为
STOP/TIDYING/TERMINATED
(rs >= SHUTDOWN
),**直接拒绝新增线程**。 -
例外情况:当状态为
SHUTDOWN
且满足以下条件时允许添加**非核心线程**处理残留任务:-
firstTask == null
(线程不携带新任务) -
workQueue
非空(队列中仍有待处理任务),确保线程关闭时仍能处理队列残留任务
-
-
2. 工作线程数调整:CAS保证并发安全
for (;;) {int wc = workerCountOf(c); // 工作线程数(低29位)// 容量校验if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))return false;// CAS递增workerCountif (compareAndIncrementWorkerCount(c))break retry; // 成功则退出整个retry循环// CAS失败后处理c = ctl.get(); // 重新读取当前ctlif (runStateOf(c) != rs)continue retry; // 状态变化则重试外层循环// 若仅workerCount变化,继续内层循环重试CAS
}
-
双重校验逻辑:
-
容量控制:通过参数
core
决定上限为核心/最大线程数,避免资源溢出。 -
CAS操作:通过
compareAndIncrementWorkerCount
原子性增加线程数,防止多线程竞争导致计数错误。 -
失败处理:若检测到状态变化(如线程池关闭),重新进行外层状态检查。若仅线程数变化,则仅重试内层循环。
-
双重循环:外层处理状态变化,内层处理线程数变更,分离关注点,提升并发效率。
-
3. 初始化变量
初始化 workerStarted
和 workerAdded
为 false
,表示工作线程未启动且未成功添加到工作线程集合。Worker
对象 w
的初始值为 null
。
4. 创建 Worker
对象并获取线程
w = new Worker(firstTask);
final Thread t = w.thread;
通过线程工厂创建 Worker
对象,并获取其绑定的线程 t
。若线程工厂创建失败(如返回 null
),后续逻辑直接跳转至第7步异常处理。
5. 加锁保证线程安全
获取 mainLock
(全局锁),确保对线程池状态的检查和修改、workers
集合的操作是原子的:
6. 启动工作线程
if (workerAdded) {t.start(); // 启动线程workerStarted = true;
}
如果 Worker
成功添加到集合,则启动其绑定的线程。
7. 异常处理
finally {if (!workerStarted)addWorkerFailed(w); // 回滚失败的 Worker 操作
}
- 若线程未启动(如锁获取后线程池已关闭),调用
addWorkerFailed
移除Worker
并更新线程池状态。
核心作用
这段代码是线程池 ThreadPoolExecutor
中添加工作线程的核心逻辑,它会创建Worker,Worker内置了一个线程(由线程工厂创建),这个线程会在Worker创建后启动,专门用于执行Worker的run()方法。另外还实现了:
-
线程安全的
Worker
管理:通过ReentrantLock
保证对共享变量(如workers
集合)的原子操作。 -
状态双重检查:防止在加锁期间线程池状态发生变化。
- 异常回滚机制:确保部分失败的操作能被正确处理,保证线程池的稳定性。
相关文章:
Java并发编程-线程池(三)
文章目录 线程池实现原理addWorker(Runnable firstTask, boolean core)1. 状态检查:校验线程池是否允许添加线程2. 工作线程数调整:CAS保证并发安全3. 初始化变量4. 创建 Worker 对象并获取线程5. 加锁保证线程安全6. 启动工作线程7. 异常处理核心作用 线…...

《黑马前端ajax+node.js+webpack+git教程》(笔记)——node.js教程+webpack教程(nodejs教程)
黑马程序员前端AJAX入门到实战全套教程,包含学前端框架必会的(ajaxnode.jswebpackgit),一套全覆盖 文章目录 Node.js与Webpack-01.Node.js入门定义和作用什么是前端工程化?(离不开node.js)Node.…...

Flink 快速入门
本文涉及到大量的底层原理知识,包括运行机制图解都非常详细,还有一些实战案例,所以导致本篇文章会比较长,内容比较多,由于内容太多,很多目录可能展示不出来,需要去细心的查看,非常适…...
高效管理多后端服务:Nginx 配置与实践指南
在现代的 Web 开发和运维中,一个系统往往由多个后端服务组成,每个服务负责不同的功能模块。例如,一个电商网站可能包括用户服务、订单服务和支付服务,每个服务都运行在独立的服务器或容器中。为了高效地管理这些服务并提供统一的访…...

阻塞队列:线程安全与生产者消费者模型解析
一、阻塞队列 阻塞队列就是基于普通队列做出扩展 1.线程安全的 如果针对一个已经满了的队列进行入队列,此时入队列操作就会阻塞,一直阻塞到队列不满(其他线程出队列元素)之后 如果针对一个已经空了的队列进行出队列,…...

【入门|Docker】基础知识扫盲:什么是 Docker?
文章目录 基础知识扫盲:什么是 Docker?Docker 是什么?Docker 核心组件Docker 与虚拟机的区别Docker 在现代开发中的核心角色Docker 的局限性 基础知识扫盲:什么是 Docker? 最近打算开始系统性地学习与云计算相关的技术…...
如何利用 Java 爬虫获得某书笔记详情:实战指南
在知识分享和学习的领域,许多平台提供了丰富的书籍笔记和学习资源。通过 Java 爬虫技术,我们可以高效地获取这些笔记的详细信息,以便进行进一步的分析和整理。本文将详细介绍如何利用 Java 爬虫获取某书笔记详情,并提供完整的代码…...

【MYSQL】基本查询,表的增删查改
📚 博主的专栏 🐧 Linux | 🖥️ C | 📊 数据结构 | 💡C 算法 | 🅒 C 语言 | 🌐 计算机网络 |🗃️ mysql 摘要:本文详细介绍了MySQL中的CRUD操作(创…...
在嵌入式系统中, 一般链路层断开多久,断开TCP为好
一、典型场景与推荐策略 1. 实时性优先(工业控制、自动化设备) 需求:快速释放资源,避免因等待重传浪费内存或阻塞任务。 策略: 立即断开:在lwip_netif_link_callback中检测到链路断开后直接关闭TCP连接&a…...

Android Studio 日志系统详解
文章目录 一、Android 日志系统基础1. Log 类2. 日志级别 二、Android Studio 中的 Logcat1. 打开 Logcat2. Logcat 界面组成3. 常用 Logcat 命令 三、高级日志技巧1. 自定义日志工具类2. 打印方法调用栈3. 打印长日志4. JSON 和 XML 格式化输出 四、Logcat 高级功能1. 自定义日…...
基于matlab的D2D 功率控制仿真
基于MATLAB的D2D(Device-to-Device)功率控制仿真示例,包含系统建模、功率控制算法实现和性能分析。该仿真以蜂窝网络为背景,重点关注D2D用户间的干扰管理和功率优化。 1. 系统模型与参数设置 clc; clear; close all;%% 参数配置…...
互联网大厂Java面试:从基础到复杂场景的技术挑战
互联网大厂Java面试:从基础到复杂场景的技术挑战 场景描述 在一家知名互联网大厂的会议室里,面试官严肃地坐在桌子的一侧,而对面则是一位充满喜感的应聘者——谢飞机。面试官准备了一系列关于Java技术栈的提问,涵盖了从基础到复…...
使用Redission来实现布隆过滤器
简述布隆过滤器 布隆过滤器是一种概率型数据结构,它可以用来判断一个元素是否在一个集合中。我们当时使用的是Redisson实现的布隆过滤器。它的底层原理是,先初始化一个比较大的数组,里面存放的是二进制0或1。一开始都是0,当一个k…...
为 Windows 和 Ubuntu 中设定代理服务器的详细方法
有时下载大模型总是下载不出来,要配置代理才行 一、Windows代理设置 ① 系统全局代理设置 打开【设置】→【网络和Internet】→【代理】。 在【手动设置代理】下,打开开关,输入: 地址:10.10.10.215 端口:…...

Feign异步模式丢失上下文问题
Feign异步模式丢失上下文问题 问题描述 当我们使用异步对我们代码进行操作优化时,代码中使用了RequestContextHolder去获取上下文的数据,当我们执行原来可以执行的业务时发现报了空指针异常或数据为空,这是为什么呢? 原理解释 …...
OpenCV阈值处理完全指南:从基础到高级应用
引言 阈值处理是图像处理中最基础、最常用的技术之一,它能够将灰度图像转换为二值图像,为后续的图像分析和处理奠定基础。本文将全面介绍OpenCV中的各种阈值处理方法,包括原理讲解、代码实现和实际应用场景。 一、什么是阈值处理࿱…...

【AWS入门】Amazon SageMaker简介
【AWS入门】Amazon SageMaker简介 [AWS Essentials] Brief Introduction to Amazon SageMaker By JacksonML 机器学习(Machine Learning,简称ML) 是当代流行的计算机科学分支技术。通常,人们在本地部署搭建环境,以满足机器学习的要求。 AWS…...
ArcGIS Pro 3.4 二次开发 - 内容
环境:ArcGIS Pro SDK 3.4 .NET 8 文章目录 内容1 工程1.1 创建一个空工程1.2 使用指定名称创建新工程1.3 使用Pro的默认设置创建新工程1.4 使用自定义模板文件创建新工程1.5 使用 ArcGIS Pro 提供的模板创建工程1.6 打开现有工程1.7 获取当前工程1.8 获取当前工程的…...
如何在 MongoDB 中设计文档结构?与关系型数据库的表结构设计有何不同?
在 MongoDB 中设计文档结构是一个核心且重要的环节,它直接影响应用的性能、可扩展性和可维护性。 MongoDB 文档结构设计原则与方法 MongoDB 的核心思想是数据如何被应用访问,就如何存储它。 嵌入 (Embedding / Denormalization) vs. 引用 (Referencing…...

MYSQL 故障排查与生产环境优化
目录 一.前置知识点 1. 案例需求 (1)mysql 常见故障解决 (2)mysql 性能优化 2.案例实施思路 (1)单库常见故障分析 (2)主从常见故障分析 (3)从几个不同…...

解决使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss“, timezone = “GMT+8“)时区转换无效的问题
前言 对于一些时间的字段,我们从数据库查询出来通常需要转换后返回给前端展示,前端需要的格式一般为yyyy-MM-dd HH:mm:ss,可以通过JsonFormat注解来作转换和时区转换。 问题场景 原因 LocalDateTime类本身不带时区信息所以转换无效 解决办…...

计算机网络概要
⽹络相关基础知识 协议 两设备之间使⽤光电信号传输信息数据 要想传递不同信息 那么⼆者ᳵ就需要约定好的数据格式 层 封装 继承 多态是计算机的性质 它们⽀持了软硬件分层的实现 同层协议可以ᳵ接通信 同层协议ᳵ不直接通信 是各⾃调⽤下层提供的结构能⼒完成通信 分层…...

Word压缩解决方案
Word压缩解决方案:基于图片压缩的 .docx 优化实践 📌 背景 在日常科研写作或项目文档整理中,Word 文档(.docx)往往因为插入大量高清图表、扫描图像、公式图等导致文件体积过大,或者毕业学位论文查重要求上…...
Spring Boot开发—— 整合Lucene构建轻量级毫秒级响应的全文检索引擎
文章目录 一、为什么选择 Lucene?轻量级搜索的底层密码二、核心原理:Lucene 的倒排索引2.1 倒排索引:速度之源2.2 段合并优化策略三、Spring Boot集成Lucene实战3.1 依赖配置3.2 实体与索引设计3.3 核心索引服务(含异常处理)3.4 使用示例(测试类)四、高级优化技巧4.1 索…...

TDengine 2025年产品路线图
TDengine OSS 之 2025 年年度路线图如下表所示。 季度功能2025Q1 虚拟表查询能力:REGEXP、GREATEST、LEAST、CAST 函数支持判断表达式、单行选择函数的其他列值、INTERP 支持插值时间范围存储能力:支持将查询结果写入超级表、超级表支持 KEEP 参数、STM…...
vue3中element-plus修改el-tooltip的宽度
注意选中的样式是 :deep(.el-popper .is-dark){max-width:30% !important; } 也有可能不需要后面的 .is-dark,看情况而定,总之不是.el-tooltip 修改el-tooltip的显示高度参考:前端字数太多使用el-table设置show-overflow-tooltip显示不下&a…...

Ubuntu服务器部署多语言项目(Node.js/Python)方式实践
Ubuntu服务器部署多语言项目(Node.js/Python)方式实践 服务器脚本运行方式命令行直接执行nohup后台执行进程 Screen概述安装基本操作命令启动 Screen退出当前会话(不终止进程)查看所有会话重连会话关闭会话 常用快捷键典型使用场景…...

计算机网络 - 2.基础协议
1.TCP协议 1.TCP(Transmission Control Protocol):传输控制协议2.TCP协议是一种面向连接的、可靠的、 基于字节流的传输层通信协议 1.面向连接:两个使用TCP协议的应用(通常一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接2.可靠的 1.数据传输之前都要建立…...
Kafka消息路由分区机制深度解析:架构设计与实现原理
一、消息路由系统的核心架构哲学 1.1 分布式系统的三元悖论 在分布式消息系统的设计过程中,架构师需要平衡三个核心诉求:数据一致性、系统可用性和分区容忍性。Kafka的分区路由机制本质上是对CAP定理的实践解: 一致性维度:通过…...
机器学习中采样哪些事
在机器学习中采样主要分为两种,过采样(Oversample)和欠采样(Undersample)。过采样就是通过增加少数类样本的数量来平衡数据集。而欠采样就是通过减少多数类样本的数量来平衡数据集。 通常在进行采样中以下是几种常用的方法: 1. 随机采样 随…...