StandardThreadExecutor源码解读与使用(tomcat的线程池实现类)

🏷️个人主页:牵着猫散步的鼠鼠 

🏷️系列专栏:Java源码解读-专栏

🏷️个人学习笔记,若有缺误,欢迎评论区指正 

目录

目录

1.前言

2.线程池基础知识回顾

2.1.线程池的组成

2.2.工作流程

2.3.Java 中的线程池实现

3.StandardThreadExecutor介绍

4.源码解读

5.使用场景

6.总结


1.前言

        这个系列已经鸽了三四个月啦,原本预期一周一更的速度,变成了一季度一更(悲),最近打算继续重拾这个专栏继续与大家分享自己的随笔,尽量做到一周一更或者一周两更,今天想和大家分享一个在工作遇到的线程池类StandardThreadExecutor。

2.线程池基础知识回顾

        首先我们来简单介绍下线程池的基础概念:

        在 Java 中,线程池是一种用于管理和复用线程的机制,能够有效提高应用程序的性能和资源利用率。线程池的核心思想是通过复用一组预先创建的线程来执行多个任务,从而减少线程创建和销毁的开销。

2.1.线程池的组成

  • 核心线程数:始终保持活跃的线程数量,即使它们处于空闲状态。
  • 最大线程数:线程池能够容纳的最大线程数量。
  • 任务队列:用于存储等待执行的任务。
  • 拒绝策略:当任务无法被执行时的处理策略。

2.2.工作流程

当有新任务提交时,如果当前线程数小于核心线程数,线程池会创建新线程执行任务。 如果核心线程都在忙,则任务被放入队列中。 当队列已满且线程数小于最大线程数时,线程池会创建新线程。 如果线程数已达到最大值且队列也满了,则根据拒绝策略处理任务。

2.3.Java 中的线程池实现

Java 提供了多种线程池实现,最常用的是 ThreadPoolExecutor,它允许我们根据需求配置线程池的各项参数。

此外,我们还可以通过 Executors 工具类创建线程池,Java 提供了几种常用的默认线程池:

  • FixedThreadPool:具有固定线程数的线程池,适用于负载较为稳定的场景。
  • CachedThreadPool:根据需要创建新线程的线程池,适用于执行大量短期异步任务。
  • ScheduledThreadPool:支持定时和周期性任务执行的线程池。
  • SingleThreadExecutor:单线程化的线程池,适用于需要顺序执行任务的场景。

3.StandardThreadExecutor介绍

        StandardThreadExecutor Apache Tomcat 中的一个线程池实现类,用于管理和调度线程的执行。它类似于 Java ThreadPoolExecutor

        既然JDK已经提供了如此多的选择,Tomcat为什么还有自己编写一个线程池实现类呢,下面就解答这个疑问

        StandardThreadExecutor Catalina 结构中的一部分,是 Tomcat 生命周期中的池化线程资源的封装。StandardThreadExecutor 是为了更好地适应 Tomcat 容器中 HTTP 请求的处理而设计的,它包含了一些特殊的优化和功能。

        他与官方线程池相比最大的区别为内部任务的执行逻辑,JDK默认的线程池的execute方法执行逻辑如下:

1.任务数小于等于核心线程数:使用核心线程执行;
2.任务数大于核心线程数:加入任务等待队列等待;
3.队列满且任务数小于最大线程数:有空闲线程使用空闲线程执行,没有的话,创建非核心线程执行;

4.任务数大于最大核线程数:执行拒绝策略

我们这里为了方便理解可以简单表达为:核心线程 -> 等待队列 ->非核心线程 ->拒绝策略

对这一块不太了解的,可以去看看博主之前的线程池文章,参照官方线程池思想编写的简易线程池,方便大家理解线程池的执行逻辑
Java手搓线程池_牵着猫散步的鼠鼠的博客-CSDN博客

而 StandardThreadExecutor其中的执行逻辑如下:

1.任务数小于等于核心线程数:使用核心线程执行;
2.任务数大于核心线程数:创建非核心线程执行;
3.任务数大于最大核线程数:加入任务等待队列等待;

4.任务数大于最大核线程数且等待队列满了:执行拒绝策略

我们这里为了方便理解简单表达为:核心线程 -> 非核心线程 -> 等待队列 -> 拒绝策略

        我们可以看到,StandardThreadExecutor的执行逻辑主要是将创建非核心线程执行这一步放到了加入等待队列等待前面,等待队列只是作为一个靠后的兜底处理,我们举一个具体的案例来说明。
        假如我们有如下线程池配置

         同一时间提交了20个任务,对于官方的线程池,最初的状态如下

        可以看到,由于我们等待队列的大小足够大,对于4个核心线程处理不完的16个核心线程会先加入到等待队列中等待,对于一些执行时间长的任务,长时间等待就会造成性能问题。

        而对于 StandardThreadExecutor 这个线程池实现类,最初的状态如下:

        可以看到, StandardThreadExecutor 对于核心线程执行不了的任务会直接创建非核心线程来执行,相比于官方线程池放入等待队列会有更高的执行效率,确保服务器在高负载下仍能保持良好的响应性能。

4.源码解读

        接下来我们继续深入 StandardThreadExecutor 类的源码了解其内部是如何实现的。

        StandardThreadExecutor 继承自 LifecycleMBeanBase,并实现了 Executor ResizableExecutor 接口 ,这意味着它不仅是一个线程池执行器,还可以与 Tomcat 的生命周期管理集成。

有如下关键属性: 

  • threadPriority:线程优先级,默认值为 5。
  • daemon:线程是否为守护线程,默认值为 true。 namePrefix:线程名称前缀,用于标识线程。
  • maxThreads 和 minSpareThreads:最大线程数和最小空闲线程数。
  • maxIdleTime:线程最大空闲时间。
  • maxQueueSize:任务队列的最大容量。
  • threadRenewalDelay:线程重生延迟时间。
  • taskqueue:任务队列,用于存储等待执行的任务。
  • executor:核心的 ThreadPoolExecutor 实例,负责管理线程的创建和任务的调度。

StandardThreadExecutor关键方法有三个,分别是

  • startInternal():启动线程池,初始化 TaskQueue 和 ThreadPoolExecutor。
  • stopInternal():停止线程池并清理资源
  • execute(Runnable command):提交任务给线程池执行。 如果线程池未启动,抛出异常。

其中 stopInternal() execute(Runnable command) 两个方法没有太多逻辑,我们主要关注 startInternal() 启动线程池这一步初始化操作,startInternal() 方法如下:

    @Override
    protected void startInternal() throws LifecycleException {

        taskqueue = new TaskQueue(maxQueueSize);
        TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
        executor.setThreadRenewalDelay(threadRenewalDelay);
        taskqueue.setParent(executor);

        setState(LifecycleState.STARTING);
    }

 startInternal() 主要是进行了 taskqueue 任务队列和 executor 线程池的初始化,我们接着查看 taskqueue 的实现

 TaskQueue 继承自 LinkedBlockingQueue<Runnable>,接着我们可以发现TaskQueue重写了offer方法

这里我们可以看到, TaskQueue 在调用父类 offer 方法前添加了许多条件判断,这里其实就是 StandardThreadExecutor 调整任务提交顺序的代码实现位置,

@Override
public boolean offer(Runnable o) {
    // 首先检查线程池的状态。
    if (parent==null) {
        return super.offer(o);
    }
    // 如果当前线程池中的线程数达到最大值,则直接将任务加入队列。
    if (parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()) {
        return super.offer(o);
    }
    // 如果提交的任务数小于或等于当前线程池的线程数,则将任务加入队列。
    if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) {
        return super.offer(o);
    }
    // 如果当前线程数小于最大线程数,则返回 false,促使 ThreadPoolExecutor 创建新线程。
    if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {
        return false;
    }
    //if we reached here, we need to add it to the queue
    return super.offer(o);
}

        当前线程数小于最大线程数时,线程池实例调用 TaskQueue offer() 方法会返回 false,此时线程池会判定任务队列满了,就会去创建新线程来执行任务。

5.使用场景

在像 Tomcat 这样的应用服务器中,用于处理大量并发请求。StandardThreadExecutor 可以有效管理线程的创建和销毁,提升服务器的响应能力和资源利用效率。

此外,在一些高并发对响应速度要求较高的场景,StandardThreadExecutor 可以有效避免任务过多积压在队列中,提高任务的响应速度,但是要注意配置合理的核心线程数和最大线程数,尽量减少线程的频繁创建和销毁。

6.总结

        StandardThreadExecutor Apache Tomcat 中的一个线程池实现类,是 Tomcat 生命周期中的池化线程资源的封装。StandardThreadExecutor 是为了更好地适应 Tomcat 容器中 HTTP 请求的处理而设计的。通过优先创建非核心线程来执行任务,避免了任务在等待队列中长时间积压,从而提升了服务器的响应速度。

      StandardThreadExecutor 内部主要通过自定义 TaskQueue 任务队列,·继承普通任务队列冰重写 offer() 方法,添加了线程数小于最大线程数的判断,巧妙的调整了任务提交的顺序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VBA字典与数组第二十讲:如何在代码运行时创建数组

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

J2:ResNet50v2算法实战与解析

J2周&#xff1a;ResNet50V2算法实战与解析 论文解读1、ResNetV2结构与ResNet结构对比☕2、关于残差结构的不同尝试☕3、关于激活的尝试☕ Pytorch实现ResNet50V2算法1、导入库并设置GPU2、导入和检查数据3、划分数据集4、搭建ResNet-50V2模型Residual BlockStack&#xff08;堆…

MFC图形函数学习04——画矩形函数

MFC中绘制矩形函数是MFC的基本绘图函数&#xff0c;它的大小和位置由左上角和右下角的坐标决定&#xff1b;若想绘制的矩形边框线型、线宽、颜色以及填充颜色都还需要其它函数的配合。 一、绘制矩形函数 原型&#xff1a;BOOL Rectangle(int x1,int y1,int x2,int y2); …

新手BUG:在声明了返回值的函数中不写返回值

本文对两个分别以int和string为返回值类型的函数进行分析&#xff0c;说明了在有返回值的函数中不写返回值会产生的问题。然后给出在编译阶段检查出这样的问题的办法。 一、背景 在软件测试环节发现&#xff0c;函数会在返回之前coredump。经过排查发现&#xff0c;在这个会…

机器人技术革新:人工智能的强力驱动

内容概要 在当今世界&#xff0c;机器人技术与人工智能的结合正如星星与大海&#xff0c;彼此辉映。随着科技的不断进步&#xff0c;人工智能不仅仅是为机器人赋予了“聪明的大脑”&#xff0c;更是推动了整个行业的快速发展。回顾机器人技术的发展历程&#xff0c;我们会发现…

外网访问 Immich 照片管理软件

Immich 是一个自托管的照片和视频备份的平台&#xff0c;它允许用户在私有服务器上存储、管理和分享他们的照片&#xff0c;视频等媒体文件。 第一步&#xff0c;本地部署安装 Immich 1&#xff0c;检查 Docker 服务状态&#xff0c;确保 Docker 正常运行。 systemctl statu…

电脑软件:推荐一款免费且实用的电脑开关机小工具

目录 一、软件简介 二、软件功能 三、软件特点 四、使用说明 五、软件下载 今天给大家推荐一款免费且实用的电脑开关机小工具KShutdown&#xff0c;有需要的朋友可以下载试一下&#xff01; 一、软件简介 KShutdown是一款精巧且实用的定时自动关机小工具&#xff0c;对于…

Manus在虚拟现实仿真模拟中的应用案例分享

Manus虚拟现实手套作为一种高精度的人机交互设备&#xff0c;在仿真模拟领域展现出了巨大的应用潜力。通过提供实时、准确的手指动作捕捉数据&#xff0c;Manus手套为多个行业带来了前所未有的仿真体验&#xff0c;推动了技术发展和应用创新。 技术特点 1. 高精度手指跟踪 Ma…

ensp中acl的使用

拓扑图及其要求如下 基础配置 检查此上R2配置错误&#xff0c;undo重新写 检查手写配置无误 按要求写配置 要求1完成 因为一个接口的入或者出方向上 只能调用一张acl表格&#xff0c;所以要求二照样在R1上面写 要求3

5. STM32之TIM实验--输出比较(PWM输出,电机,四轴飞行器,智能车,机器人)--(实验5:PWM驱动直流电机)

作者:Whappy,日期:2024.10.29,决战STM32 直流电机的控制就比较简单了,只有数据线和地线,正接正转,反接反转,为了方便,本实验采用H桥电路来控制电机的正反转,H桥电路也很简单,就是4个MOS管构成的2路推挽输出电路. 注:基本上大功率器件,单片机基本上是无法驱动的,都是要靠一部分…

Python基础知识汇总(建议收藏再观看)!

1.执行脚本的两种方式 Python a.py 直接调用Python解释器执行文件 chomd x a.py ./a.py #修改a.py文件的属性&#xff0c;为可执行&#xff0c;在用 ./ 执行a.py 文件 2、简述位、字节的关系 1bytes8bit ,2**8256,可以代表256中变化&#xff0c; 3、简述 ascii、unicode、…

Java中IO的高级操作

目录 缓冲流 缓冲字节输入流&#xff1a; 缓冲字节输出流&#xff1a; 缓冲字符输入流&#xff1a; 缓冲字符输出流&#xff1a; 转换流 转换流字符输入&#xff1a; 转换流字符输出&#xff1a; 练习案例&#xff1a; 打印流 字节打印流&#xff1a; 字符打印流&a…

Matlab高光谱遥感

原文链接&#xff1a;Matlab高光谱遥感https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247623643&idx5&sne4557ed43728f851140b100f42286988&chksmfa8da23ccdfa2b2a4d795bf4087f672faaa7082d1f52e046616ab7bf196a6eef89ea553d06b1&token1392391660&…

ssm+jsp663数学课程评价系统的设计与开发

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…

openssl-ec-chn命令手册

openssl-ec命令处理EC&#xff08;Elliptic Curve&#xff0c;椭圆曲线&#xff09;密钥。使密钥可以在各种形式之间转换&#xff0c;并打印出其组件。注意&#xff1a;OpenSSL使用“SEC 1:椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;”中指定的私钥格…

(JVM)深入JAVA底层 JVM(Java 虚拟机)!带你认识JVM、程序计数器、JVM栈和方法栈还有堆内存!看看JAVA针对这些内存空间都做了什么吧!

1. 什么是JVM java 二进制字节码的运行环境&#xff0c;简称&#xff1a;java 虚拟机&#xff08;Java Virtual Machine&#xff09; 2. 好处是什么 一次编写&#xff0c;到处运行自动内存管理&#xff0c;GC垃圾回收功能数组下标越界检查多态… 3. jdk、jre、jvm 4. 学习J…

OLAP平台架构演化历程

OLAP平台架构演化历程 0 导读 随着大数据的持续发展及数字化转型的兴起&#xff0c;大数据OLAP分析需求越来越迫切&#xff0c;不论是大型互联网企业&#xff0c;还是中小型传统企业&#xff0c;都在积极探索及实践OLAP引擎选型及平台架构建设&#xff0c;大数据技术的蓬勃发展…

Kaggle入门指南(Kaggle竞赛)

文章目录 Kaggle 入门指南1. Kaggle 的功能概述1.1 竞赛1.2 数据集1.3 学习与教程1.4 社区 2. 注册与设置2.1 创建账户2.2 完善个人资料 3. 探索数据集3.1 查找数据集3.2 下载数据集示例代码&#xff1a;加载数据集 3.3 数据预处理示例代码&#xff1a;数据预处理 4. 参与竞赛4…

docker 可用镜像服务地址(2024.10.31亲测可用)

1.错误 Error response from daemon: Get “https://registry-1.docker.io/v2/” 原因&#xff1a;镜像服务器地址不可用。 2.可用地址 编辑daemon.json&#xff1a; vi /etc/docker/daemon.json内容修改如下&#xff1a; {"registry-mirrors": ["https://…

TortoiseSVN小乌龟下载安装(Windows11)

目录 TortoiseSVN 1.14.7工具下载安装 TortoiseSVN 1.14.7 工具 系统&#xff1a;Windows 11 下载 官网&#xff1a;https://tortoisesvn.subversion.org.cn/downloads.html如图选 TortoiseSVN 1.14.7 - 64 位 下载完成 安装 打开 next&#xff0c;next Browse&#xf…