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

news2024/11/6 8:05:29

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

🏷️系列专栏: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() 方法,添加了线程数小于最大线程数的判断,巧妙的调整了任务提交的顺序。

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

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

相关文章

解决pytorch问题:received an invalid combination of arguments - got

问题表现 今天跑模型时报了一个非常奇怪的错误&#xff1a; 意思是“你的lstm层输入的参数是无效的&#xff0c;要求接收参数的类型是(Tensor, tuple of (Tensor, Tensor), list of [Parameter, Parameter, Parameter, Parameter], float, int, float, bool, bool, bool)&am…

Springboot与easypoi(2):合并单元格、二级表头、动态导出

一、纵向合并单元格 使用Excel(needMerge true)标记的属性表示此单元格需要合并。ExcelCollection表示一对多的集合&#xff0c;下面是合并单元格案例。 实体类 企业类&#xff1a; package com.ywz.entity;import cn.afterturn.easypoi.excel.annotation.Excel; import cn.…

vue3 + ts + element-plus 二次封装 el-table

一、实现效果&#xff1a; &#xff08;1&#xff09;数据为空时&#xff1a; &#xff08;2&#xff09;有数据时&#xff1a;存在数据合并&#xff1b;可自定义表头和列的内容 &#xff08;3&#xff09;新增行&#xff1a; &#xff08;4&#xff09;删除行&#xff1a; &a…

Python CGI编程-cookie的设置、检索

设置检索 其他&#xff1a; 1. http.cookies和http.cookiejar区别&#xff1a; http.cookies主要用于创建和操作单个cookie对象&#xff0c;适用于需要精细控制单个cookie属性的场景。http.cookiejar则用于管理多个cookie&#xff0c;适用于需要自动处理多个请求和响应中的coo…

k8s 二进制部署安装(三)

目录 部署Calico Calico 工作原理 部署Calico 部署CoreDNS 负载均衡部署 部署dashboard 部署Calico 安装步骤来到 CNI 网络组件 &#xff0c;在&#xff08;二&#xff09;中我们安装了flannel&#xff0c;现在我们要尝试安装另一网络组件Calico calico 不使用隧道或NAT…

idea 创建web工程

模块添加Add Framework Support web Application 改为4.0以上

Linux系列-进程的概念

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 这篇文章&#xff0c;我们主要分析一下进程。 之前&#xff0c;我们讲过了冯诺依曼体系架构&#xff0c; 我们常见的计算机&#xff0c;像笔记本&#xff0c;或者不常见的计算机…

Rust的enum枚举的强大用法

在Rust中&#xff0c;enum&#xff08;枚举&#xff09;是一种非常强大的类型&#xff0c;它可以包含多个变体&#xff08;variants&#xff09;&#xff0c;每个变体可以是不同的类型&#xff0c;包括复杂类型。这使得enum在Rust中不仅用于表示简单的状态或选项集合&#xff0…

【初识Linux】

十点半的飞机快要到了&#xff0c;机场还是那么的拥挤.......................................................................... 文章目录 一、【初识指令】 1、 ls✔ 2、pwd✔ 3、cd✔ 4. touch✔ 5.mkdir✔ 6.rmdir✔ 7、rm ✔ 8.man✔ 9.cp✔ ​10、mv✔ 11、cat✔…

头歌——算法设计与分析(贪心法)

文章目录 第1关&#xff1a;贪心法代码 第2关&#xff1a;最小生成树代码 第3关&#xff1a;Huffman 编码代码 第4关&#xff1a;单源点最短路径代码 第1关&#xff1a;贪心法 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;贪心法 ;。 贪心法&#xff0c;又称…

面试题:JVM(四)

new对象流程&#xff1f;&#xff08;龙湖地产&#xff09; 对象创建方法&#xff0c;对象的内存分配。&#xff08;360安全&#xff09; 1. 对象实例化 创建对象的方式有几种&#xff1f; 创建对象的步骤 指针碰撞&#xff1a;以指针为分界线&#xff0c;一边是已连续使用的…

【进阶sql】复杂sql收集及解析【mysql】

开发时会出现&#xff0c;必须写一些较复杂sql的场景 可能是给会sql的客户 提供一些统计sql 或是临时需要统计数据信息但是 开发一个统计功能有来不及的情况 也可能是报表系统组件 只支持 sql统计的情况 特地记录下这些sql 作为积累 substring 截取查询出的字符串&#xff…

Python实现全国岗位招聘信息可视化分析(源码+论文+部署讲解)

项目源码&数据源获取 利用Python实现全国岗位招聘信息可视化分析 项目背景&#xff1a; 1.为企业招聘决策提供科学的依据和参考&#xff0c;可以帮助人力资源部门、招聘机构和求职者了解当前的就业形势、行业趋势和人才需求&#xff0c;从而做出更明智的招聘和求职决策。…

Python毕业设计选题:基于django+vue的宠物寄养平台的设计与实现

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 1. 前台系统功能模块 系统首页界面 用户注册界面 用户登录界面 宠物商城界面 宠物店…

Linux云计算 |【第五阶段】CLOUD-DAY10

主要内容&#xff1a; 部署Dashboard、部署Prometheus、部署HPA集群 一、Dashboard介绍 Dashboard是基于网页的Kubernetes用户界面&#xff0c;可以使用Dashboard将容器应用部署到Kubernetes集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。可以使用Da…

将Notepad++添加到右键菜单【一招实现】

一键添加注册表 复制以下代码保存为 Notepad.reg&#xff0c;将红框内路径修改为自己电脑的“Notepad.exe路径”后&#xff0c;再双击运行即可。 Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\*\shell\NotePad] "Notepad" "Icon""D:\\N…

Git下载-连接码云-保姆级教学(连接Gitee失败的解决)

Git介绍 码云连接 一、Git介绍 二、Git的工作机制 下载链接&#xff1a;Git - 下载软件包 三、使用步骤 创建一个wss的文件夹&#xff0c;作为‘工作空间’ 四、连接码云账号 五、连接Gitee失败的解决方法 一、Git介绍 Git是一个免费的、开源的分布式版本控制…

Spring Boot 与 Vue 共铸卓越采购管理新平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

DataFlow v202410 版本更新 一站式数据处理平台

DataFlow 是 OpenCSG 推出的一站式数据处理平台&#xff0c;与 CSGHub 无缝集成&#xff0c;形成数据到模型的全生命周期闭环&#xff0c;助力持续优化。平台兼容多种数据格式与来源&#xff0c;支持本地、云端和网络数据接入&#xff0c;并提供高效转换和读取工具&#xff0c;…

【搜索引擎】俄罗斯搜索引擎yandex

俄罗斯搜索引擎yandex 1997年&#xff0c;俄罗斯搜索引擎Yandex&#xff08;俄语意为&#xff1a;语言目录&#xff09;首次上线&#xff0c;已发展成为全球第四大搜索引擎和第二大非英语搜索引擎 https://yandex.com/