Tomcat线程池原理(下篇:工作原理)
文章目录
- 前言
- 正文
- 一、执行线程的基本流程
- 1.1 JUC中的线程池执行线程
- 1.2 Tomcat 中线程池执行线程
- 二、被改造的阻塞队列
- 2.1 TaskQueue的 offer(...)
- 2.2 TaskQueue的 force(...)
- 三、总结
前言
Tomcat 线程池,是依据 JUC 中的线程池 ThreadPoolExecutor
重新自定义实现的。
其执行线程的代码逻辑,和JUC 中是相同的。主要区别在于,Tomcat中对 阻塞队列进行了改造。
本文主要研究 Tomcat 的线程池是如何执行线程的,即线程池的工作原理。
同系列文章:Tomcat线程池原理(上篇:初始化原理)
正文
一、执行线程的基本流程
Tomcat 中执行线程的基本流程,和JUC中是一致的。
以下贴出两种执行方法。
1.1 JUC中的线程池执行线程
在ThreadPoolExecutor
中,执行线程的方法定义如下:
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);
}
1.2 Tomcat 中线程池执行线程
@Override
public void execute(Runnable command) {execute(command,0,TimeUnit.MILLISECONDS);
}
重载方法定义如下:
这个方法被Deprecated
标注,源码中注释中描述说是,Tomcat10中会被删除,估计是一种优化。本文不做研究。
@Deprecated
public void execute(Runnable command, long timeout, TimeUnit unit) {// 提交的任务数量+1submittedCount.incrementAndGet();try {// 执行线程任务(这个方法中的代码和JUC中执行线程的代码一致,差别在于,阻塞队列使用了Tomcat自定义的队列)executeInternal(command);} catch (RejectedExecutionException rx) {// 被拒绝后,尝试将线程放入队列// 如果当前队列是Tomcat自定义的队列TaskQueue,尝试将任务放入队列if (getQueue() instanceof TaskQueue) {final TaskQueue queue = (TaskQueue) getQueue();try {// 强制将线程任务放入阻塞队列if (!queue.force(command, timeout, unit)) {// 放入队列失败,提交的任务数-1submittedCount.decrementAndGet();// 抛出异常,线程池队列已满throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));}} catch (InterruptedException x) {// 中断时,提交的任务数-1submittedCount.decrementAndGet();// 抛出异常throw new RejectedExecutionException(x);}} else {// 当前指定的队列不是TaskQueue,提交的任务数-1,并抛出异常submittedCount.decrementAndGet();throw rx;}}
}
另外,executeInternal
的内容如下(和JUC中基本一致):区别在于阻塞队列换成了TaskQueue
private void executeInternal(Runnable command) {if (command == null) {throw new NullPointerException();}int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true)) {return;}c = ctl.get();}// 这里的workQueue,实际已经换成了TaskQueueif (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command)) {reject(command);} else if (workerCountOf(recheck) == 0) {addWorker(null, false);}}else if (!addWorker(command, false)) {reject(command);}
}
二、被改造的阻塞队列
从第一小节中,看到的东西并不多。因为,Tomcat线程池的改造重心,在于阻塞队列,也就是 TaskQueue
。
2.1 TaskQueue的 offer(…)
@Override
public boolean offer(Runnable o) {//we can't do any checksif (parent==null) {return super.offer(o);}// 若是达到最大线程数,直接进队列if (parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()) {return super.offer(o);}// 已提交任务数小于等于当前线程数// 对应的场景是:当前活跃线程为10个,但是只有8个任务在执行,于是,直接进队列if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) {return super.offer(o);}// 当前线程数小于最大线程数,此次拒绝进入队列if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {return false;}//if we reached here, we need to add it to the queuereturn super.offer(o);
}
2.2 TaskQueue的 force(…)
其本质是,直接入队列。
public boolean force(Runnable o) {if (parent == null || parent.isShutdown()) {throw new RejectedExecutionException(sm.getString("taskQueue.notRunning"));}return super.offer(o); //forces the item onto the queue, to be used if the task is rejected
}
三、总结
根据源码中对于TaskQueue的改造,可以观察出,Tomcat线程池的主要特点如下:
- 当前线程数小于corePoolSize,则去创建工作线程;
- 当前线程数大于corePoolSize,但小于maximumPoolSize,则去创建工作线程;
- 当前线程数大于maximumPoolSize,则将任务放入到阻塞队列中,当阻塞队列满了之后,则调用拒绝策略丢弃任务;
- 任务执行失败时不会直接抛出错误,而是装回队列里再次尝试执行;
- 当线程池没有达到最大执行线程的时候,会优先开线程再使用任务队列;
总结之后就是,Tomcat 为了更适配 IO 密集型任务,改造了阻塞队列。与JUC相比,会先去创建线程执行任务,创建的线程数达到最大线程数时,再放入队列等待空闲线程的出现。
相关文章:
Tomcat线程池原理(下篇:工作原理)
文章目录 前言正文一、执行线程的基本流程1.1 JUC中的线程池执行线程1.2 Tomcat 中线程池执行线程 二、被改造的阻塞队列2.1 TaskQueue的 offer(...)2.2 TaskQueue的 force(...) 三、总结 前言 Tomcat 线程池,是依据 JUC 中的线程池 ThreadPoolExecutor 重新自定义…...

【服务器数据恢复】通过reed-solomon算法恢复raid6数据的案例
服务器数据恢复环境: 一台网站服务器中有一组由6块磁盘组建的RAID6磁盘阵列,操作系统层面运行MySQL数据库和存放一些其他类型文件。 服务器故障: 该服务器在工作过程中,raid6磁盘阵列中有两块磁盘先后离线,不知道是管理…...

LeetCode 2583.二叉树中的第 K 大层和:层序遍历 + 排序
【LetMeFly】2583.二叉树中的第 K 大层和:层序遍历 排序 力扣题目链接:https://leetcode.cn/problems/kth-largest-sum-in-a-binary-tree/ 给你一棵二叉树的根节点 root 和一个正整数 k 。 树中的 层和 是指 同一层 上节点值的总和。 返回树中第 k …...

element ui 安装 简易过程 已解决
我之所以将Element归类为Vue.js,其主要原因是Element是(饿了么团队)基于MVVM框架Vue开源出来的一套前端ui组件。我最爱的就是它的布局容器!!! 下面进入正题: 1、Element的安装 首先你需要创建…...
websoket
WebSockets 是一种先进的技术。它可以在用户的浏览器和服务器之间打开交互式通信会话。你可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应,比较典型的应用场景就是即时通讯(聊天)系统。 <!DOC…...
案例:微服务从Java/SpringBoot迁移到Golan
基于 Java 的微服务,特别是那些使用 Spring Boot 的微服务,长期以来因其强大的功能和广泛的社区支持而闻名。Spring Boot 的约定优于配置方法简化了微服务的部署和开发,提供了大量开箱即用的功能,例如自动配置、独立功能和简单的依…...

小波变换模拟
小波变换是一种信号处理技术,通过在时间-频率域中使用基于小波的函数进行信号分析。小波变换在处理非平稳信号和图像时特别有用,可以将信号分解为不同频率的成分。它在数据压缩、去噪、特征提取等领域有广泛应用。 MATLAB中提供了用于二维离散小波变换的…...
cv::Mat图像操作
图像读写 //include header #include <opencv2/imgcodecs.hpp>/** Currently, the following file formats are supported: Windows bitmaps - *.bmp, *.dib (always supported) JPEG files - *.jpeg, *.jpg, *.jpe (see the Note section) JPEG 2000 files - *.jp2 (s…...

【机器学习基础】一元线性回归(适合初学者的保姆级文章)
🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:机器学习 欢迎订阅!后面的内容会越来越有意思~ 💡往期推荐: 【机器学习基础】机器学习入门(1) 【机器学习基…...
2024年软件测试岗位-面试
第一部分: 1、自我介绍:简历写到的快速描述,学校、学历、工作经验等(注意:不要过度优化简历,你不写别人可能会问,但你写了别人一定会问!) 第二部分: 1、功能测…...

【坑】Spring Boot整合MyBatis,一级缓存失效
一、Spring Boot整合MyBatis,一级缓存失效 1.1、概述 MyBatis一级缓存的作用域是同一个SqlSession,在同一个SqlSession中执行两次相同的查询,第一次执行完毕后,Mybatis会将查询到的数据缓存起来(缓存到内存中…...

J7 - 对于ResNeXt-50算法的思考
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制 J6周有一段代码如下 思考过程 首先看到这个问题的描述,想到的是可能使用了向量操作的广播机制然后就想想办法验证一下&…...
R3F(React Three Fiber)基础篇
之前一直在做ThreeJS方向,整理了两篇R3F(React Three Fiber)的文档,这是基础篇,如果您的业务场景需要使用R3F,您又对R3F不太了解,或者不想使用R3F全英文文档,您可以参考一下这篇&…...
torch\tensorflow在大语言模型LLM中的作用
文章目录 torch\tensorflow在大语言模型LLM中的作用 torch\tensorflow在大语言模型LLM中的作用 在大型语言模型(LLM)中,PyTorch和TensorFlow这两个深度学习框架起着至关重要的作用。它们为构建、训练和部署LLM提供了必要的工具和基础设施。 …...

设计模式-创建型模式-单例模式
0 引言 创建型模式(Creational Pattern)关注对象的创建过程,是一类最常用的设计模式,每个创建型模式都通过采用不同的解决方案来回答3个问题:创建什么(What),由谁创建(W…...

备战蓝桥杯—— 双指针技巧巧答链表1
对于单链表相关的问题,双指针技巧是一种非常广泛且有效的解决方法。以下是一些常见问题以及使用双指针技巧解决: 合并两个有序链表: 使用两个指针分别指向两个链表的头部,逐一比较节点的值,将较小的节点链接到结果链表…...
微信小程序返回上一级页面并自动刷新数据
文章目录 前言一、获取小程序栈二、生命周期触发总结 前言 界面由A到B,在由B返回A,触发刷新动作 一、获取小程序栈 界面A代码 shuaxin(){//此处可进行接口请求从而实现更新数据的效果console.log("刷新本页面数据啦")},界面B代码 // 返回触…...

Spring⼯⼚创建复杂对象
文章目录 5. Spring⼯⼚创建复杂对象5.1 什么是复杂对象5.2 Spring⼯⼚创建复杂对象的3种⽅式5.2.1 FactoryBean 接口5.2.2 实例⼯⼚5.2.3 静态工厂 5.3 Spring 工厂的总结 6. 控制Spring⼯⼚创建对象的次数6.1 如何控制简单对象的创建次数6.2 如何控制复杂对象的创建次数6.3 为…...
Top-N 泛型工具类
一、代码实现 通过封装 PriorityQueue 实现,PriorityQueue 本质上是完全二叉树实现的小根堆(相对来说,如果比较器反向比较则是大根堆)。 public class TopNUtil<E extends Comparable<E>> {private final PriorityQ…...

Java 后端面试指南
面试指南 TMD,一个后端为什么要了解那么多的知识,真是服了。啥啥都得了解 MySQL MySQL索引可能在以下几种情况下失效: 不遵循最左匹配原则:在联合索引中,如果没有使用索引的最左前缀,即查询条件中没有包含…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...

图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...