tomcat优化jvm(Tomcat修正JDK原生线程池bug的实现原理)
tomcat优化jvm
Tomcat修正JDK原生线程池bug的实现原理为提高处理能力和并发度,Web容器一般会把处理请求的任务放到线程池,而JDK的原生线程池先天适合CPU密集型任务,于是Tomcat改造之。
Tomcat 线程池原理其实ThreadPoolExecutor的参数主要有如下关键点:
限制线程个数
限制队列长度
而Tomcat对这俩资源都需要限制,否则高并发下CPU、内存都有被耗尽可能。
因此Tomcat的线程池传参:
// 定制的任务队列 taskqueue = new TaskQueue(maxQueueSize); // 定制的线程工厂 TaskThreadFactory tf = new TaskThreadFactory(namePrefix, daemon, getThreadPriority() ); // 定制线程池 executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS, taskqueue, tf);
Tomcat对线程数也有限制,设置:
- 核心线程数(minSpareThreads)
- 最大线程池数(maxThreads)
Tomcat线程池还有自己的特色任务处理流程,通过重写execute方法实现了自己的特色任务处理逻辑:
- 前corePoolSize个任务时,来一个任务就创建一个新线程
- 再有任务,就把任务放入任务队列,让所有线程去抢。若队列满,就创建临时线程
- 总线程数达到maximumPoolSize,则继续尝试把任务放入任务队列
- 若缓冲队列也满了,插入失败,执行拒绝策略
和 JDK 线程池的区别就在step3,Tomcat在线程总数达到最大数时,不是立即执行拒绝策略,而是再尝试向任务队列添加任务,添加失败后再执行拒绝策略。
具体又是如何实现的呢?
public void execute(Runnable command, long timeout, TimeUnit unit) { submittedCount.incrementAndGet(); try { // 调用JDK原生线程池的execute执行任务 super.execute(command); } catch (RejectedExecutionException rx) { // 总线程数达到maximumPoolSize后,JDK原生线程池会执行默认拒绝策略 if (super.getQueue() instanceof TaskQueue) { final TaskQueue queue = (TaskQueue)super.getQueue(); try { // 继续尝试把任务放入任务队列 if (!queue.force(command, timeout, unit)) { submittedCount.decrementAndGet(); // 若缓冲队列还是满了,插入失败,执行拒绝策略。 throw new RejectedExecutionException("..."); } } } } }
Tomcat线程池的execute方法第一行:
submittedCount.incrementAndGet();
任务执行失败,抛异常时,将该计数器减一:
submittedCount.decrementAndGet();
Tomcat线程池使用 submittedCount 变量维护已提交到线程池,但未执行完的任务数量。
为何要维护这样一个变量呢?
Tomcat的任务队列TaskQueue扩展了JDK的LinkedBlockingQueue,Tomcat给了它一个capacity,传给父类LinkedBlockingQueue的构造器。
public class TaskQueue extends LinkedBlockingQueue<Runnable> { public TaskQueue(int capacity) { super(capacity); } ... }
capacity参数通过Tomcat的maxQueueSize参数设置,但maxQueueSize默认值Integer.MAX_VALUE:当前线程数达到核心线程数后,再来任务的话线程池会把任务添加到任务队列,并且总会成功,就永远无机会创建新线程了。
为解决该问题,TaskQueue重写了LinkedBlockingQueue#offer,在合适时机返回false,表示任务添加失败,这时线程池就会创建新线程。
什么叫合适时机?
public class TaskQueue extends LinkedBlockingQueue<Runnable> { ... @Override // 线程池调用任务队列的方法时,当前线程数 > core线程数 public boolean offer(Runnable o) { // 若线程数已达max,则不能创建新线程,只能放入任务队列 if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o); // 至此,表明 max线程数 > 当前线程数 > core线程数 // 说明可创建新线程: // 1. 若已提交任务数 < 当前线程数 // 表明还有空闲线程,无需创建新线程 if (parent.getSubmittedCount()<=(parent.getPoolSize())) return super.offer(o); // 2. 若已提交任务数 > 当前线程数 // 线程不够用了,返回false去创建新线程 if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false; // 默认情况下总是把任务放入任务队列 return super.offer(o); } }
所以Tomcat维护 已提交任务数 是为了在任务队列长度无限时,让线程池还能有机会创建新线程。
到此这篇关于Tomcat是如何修正JDK原生线程池bug的的文章就介绍到这了,更多相关Tomcat JDK原生线程池内容请搜索开心学习网以前的文章或继续浏览下面的相关文章希望大家以后多多支持开心学习网!
- spring boot 如何启动tomcat(传统tomcat启动服务与springboot启动内置tomcat服务的区别推荐)
- idea配置tomcat启动web项目(如何在IntelliJ IDEA 2018上配置Tomcat并运行第一个JavaWeb项目)
- maven默认插件配置(Maven使用tomcat8-maven-plugin插件的详细教程)
- docker启动项目需要tomcat吗(docker安装tomcat并部署Springboot项目war包的方法)
- 如何设置tomcat的jvm(Tomcatc3p0配置jnid数据源2种实现方法解析)
- servlet与tomcat区别(深入了解tomcat中servlet的创建方式实现)
- tomcat架构解析(浅谈Tomcat多层容器的设计)
- tomcat的startup闪退的原因(直接双击启动tomcat中的startup.bat闪退原因及解决方法)
- linux部署tomcat项目(阿里云服务器linux系统搭建Tomcat部署Web项目)
- Tomcat和Weblogic部署纯html文件过程解析(Tomcat和Weblogic部署纯html文件过程解析)
- tomcat需要修改哪几个端口参数(详解tomcat各个端口的作用)
- tomcat运行中找不到路径(Tomcat将配置文件放在外部的解决方法)
- 如何认识服务器(关于Nginx、Apache、Tomcat三个WEB服务器的区别和认知)
- tomcat服务如何在eclipse中配置(HBuilderX配置tomcat外部服务器查看编辑jsp界面的方法详解)
- tomcat解决乱码(解决tomcat 静态页面html中文乱码的解决终极篇)
- tomcat怎么知道访问servlet(详解从源码分析tomcat如何调用Servlet的初始化)
- 吉林舒兰 封城 15人确诊 276人隔离,出现跨省传播(吉林舒兰封城)
- 四月新番CP人气榜公布,《剃须》两度上榜,沙优不是女朋友(四月新番CP人气榜公布)
- 2019年外媒秋季新番动画角色CP排行榜,桐人和爱丽丝落榜(2019年外媒秋季新番动画角色CP排行榜)
- 新一小兰领衔 盘点动漫中的那些 远距离恋爱情侣(盘点动漫中的那些)
- 大事件 合肥四中火了(大事件合肥四中火了)
- 翼龙贷组织出借人调研 感受鄱阳 借 来的致富路(翼龙贷组织出借人调研)
热门推荐
- docker容器无法启动(Docker 无法停止或删除容器服务问题的解决方案)
- 织梦栏目怎么管理(织梦“模块管理”里面的“模块列表”显示空白的解决方法)
- NameValueCollection用法
- linux如何设置sudo授权(如何在Linux环境为用户添加sudo权限)
- 安装phpstudy注意哪些问题(phpstudy怎么卸载?phpstudy卸载图文教程)
- python计算csv的行数(对Python 多线程统计所有csv文件的行数方法详解)
- <i>和<em>、<b>和<strong>的区别
- php查询数据库给变量赋值(详解PHP变量传值赋值和引用赋值变量销毁)
- sqlserver 存储过程参数类型(详解SQL Server表和索引存储结构)
- python常用列表函数和方法(Python enumerate函数功能与用法示例)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9