前提

Tomcat 10.1.x

Tomcat线程池介绍

Tomcat线程池,源于JAVA JDK自带线程池。由于JAVA JDK线程池策略,比较适合处理 CPU 密集型任务,但是对于 I/O 密集型任务,如数据库查询,rpc 请求调用等,不是很友好,所以Tomcat在其基础上进行了扩展。

任务处理流程

扩展线程池相关源码简析

Tomcat中定义了一个StandardThreadExecutor类,该类实现了org.apache.catalina.Executororg.apache.tomcat.util.threads.ResizableExecutor接口

该类内部定义了namePrefix(创建的线程名称前缀,默认值tomcat-exec-),maxThreads(最大线程数,默认值 200),minSpareThreads(最小线程数,即核心线程数,默认值 25),maxIdleTime(线程最大空闲时间,毫秒为单位,默认值60秒),maxQueueSize(最大队列大小,默认值 Integer.MAX_VALUE)等属性,此外,还定义了一个org.apache.tomcat.util.threads.ThreadPoolExecutor类型的执行器对象,一个execute(Runnable command) 方法

execute(Runnable command) 方法被调用时,会调用上述ThreadPoolExecutor类对象的execute方法

org.apache.catalina.core.StandardThreadExecutor.java

import org.apache.catalina.Executor;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.threads.ResizableExecutor;
import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor; public class StandardThreadExecutor extends LifecycleMBeanBase
implements Executor, ResizableExecutor { protected static final StringManager sm = StringManager.getManager(StandardThreadExecutor.class); // ---------------------------------------------- Properties
/**
* Default thread priority
*/
protected int threadPriority = Thread.NORM_PRIORITY; /**
* Run threads in daemon or non-daemon state
*/
protected boolean daemon = true; /**
* Default name prefix for the thread name
*/
protected String namePrefix = "tomcat-exec-"; /**
* max number of threads
*/
protected int maxThreads = 200; /**
* min number of threads
*/
protected int minSpareThreads = 25; /**
* idle time in milliseconds
*/
protected int maxIdleTime = 60000; /**
* The executor we use for this component
*/
protected ThreadPoolExecutor executor = null; /**
* the name of this thread pool
*/
protected String name; /**
* The maximum number of elements that can queue up before we reject them
*/
protected int maxQueueSize = Integer.MAX_VALUE; /**
* After a context is stopped, threads in the pool are renewed. To avoid
* renewing all threads at the same time, this delay is observed between 2
* threads being renewed.
*/
protected long threadRenewalDelay =
org.apache.tomcat.util.threads.Constants.DEFAULT_THREAD_RENEWAL_DELAY; private TaskQueue taskqueue = null;
// ---------------------------------------------- Constructors
public StandardThreadExecutor() {
//empty constructor for the digester
} //....此处代码已省略 @Override
public void execute(Runnable command) {
if (executor != null) {
// Note any RejectedExecutionException due to the use of TaskQueue
// will be handled by the o.a.t.u.threads.ThreadPoolExecutor
executor.execute(command);
} else {
throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
}
} //....此处代码已省略 }

org.apache.tomcat.util.threads.ThreadPoolExecuto类对象的execute(Runnable command) 方法被调用时,会调用该类定义的一个executeInternal方法,并在捕获到RejectedExecutionException异常时,尝试再次将任务放入工作队列中。

executeInternal方法中,通过代码可知,当前线程数小于核心线程池大小时,会创建新线程,否则,会调用workQueue对象(org.apache.tomcat.util.threads.TaskQueue类型)的offer方法,将任务进行排队。Tomcat通过控制workQueue.offer()方法的返回值,实现了当前线程数超过核心线程池大小时,优先创建线程,而不是让任务排队。

org.apache.tomcat.util.threads.ThreadPoolExecutor

public class ThreadPoolExecutor extends AbstractExecutorService {
//...此处代码已省略
@Override
public void execute(Runnable command) {
submittedCount.incrementAndGet();
try {
executeInternal(command);
} catch (RejectedExecutionException rx) {
if (getQueue() instanceof TaskQueue) {
// If the Executor is close to maximum pool size, concurrent
// calls to execute() may result (due to Tomcat's use of
// TaskQueue) in some tasks being rejected rather than queued.
// If this happens, add them to the queue.
final TaskQueue queue = (TaskQueue) getQueue();
if (!queue.force(command)) {
submittedCount.decrementAndGet();
throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));
}
} else {
submittedCount.decrementAndGet();
throw rx;
}
}
} /**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@link RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
private void executeInternal(Runnable command) {
if (command == null) {
throw new NullPointerException();
}
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) { // 当前线程数小于核心线程数时,
if (addWorker(command, true)) { // 创建线程
return;
}
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { //workQueue.offer(command)为false时,会走以下的else if分支,创建线程
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);
}
} //...此处代码已省略 }

org.apache.tomcat.util.threads.TaskQueue继承于java.util.concurrent.LinkedBlockingQueue,并重写了offer(排队任务的方法),该方法中,当当前线程数大于核心线程数,小于最大线程数时,返回false,导致上述executeInternal方法中workQueue.offer(command)false,进而导致该分支代码不被执行,执行addWorker(command, false)方法,创建新线程。

org.apache.tomcat.util.threads.TaskQueue

import java.util.Collection;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit; import org.apache.tomcat.util.res.StringManager; /**
* As task queue specifically designed to run with a thread pool executor. The
* task queue is optimised to properly utilize threads within a thread pool
* executor. If you use a normal queue, the executor will spawn threads when
* there are idle threads and you won't be able to force items onto the queue
* itself.
*/
public class TaskQueue extends LinkedBlockingQueue<Runnable> { //...此处代码已省略 /**
* Used to add a task to the queue if the task has been rejected by the Executor.
*
* @param o The task to add to the queue
*
* @return {@code true} if the task was added to the queue,
* otherwise {@code false}
*/
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
} @Override
public boolean offer(Runnable o) {
//we can't do any checks
if (parent==null) {
return super.offer(o);
}
//we are maxed out on threads, simply queue the object
if (parent.getPoolSize() == parent.getMaximumPoolSize()) {
return super.offer(o);
}
//we have idle threads, just add it to the queue
if (parent.getSubmittedCount()<=(parent.getPoolSize())) {
return super.offer(o);
}
//if we have less threads than maximum force creation of a new thread
if (parent.getPoolSize()<parent.getMaximumPoolSize()) {
return false;
}
//if we reached here, we need to add it to the queue
return super.offer(o);
}
//...此处代码已省略
}

参考链接

https://gitee.com/apache/tomcat/blob/10.1.x/java/org/apache/catalina/core/StandardThreadExecutor.java

Tomcat 线程池学习总结的更多相关文章

  1. Java线程池学习

    Java线程池学习 Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java ...

  2. 详解Tomcat线程池原理及参数释义

    omcat线程池有如下参数: maxThreads, 最大线程数,tomcat能创建来处理请求的最大线程数 maxSpareTHreads, 最大空闲线程数,在最大空闲时间内活跃过,但现在处于空闲,若 ...

  3. Tomcat线程池的深入理解

    1.工作机制: Tomcat启动时如果没有请求过来,那么线程数(都是指线程池的)为0: 一旦有请求,Tomcat会初始化minSpareThreads设置的线程数: 2.线程池作用: Tomcat的线 ...

  4. 【Java多线程】线程池学习

    Java线程池学习 众所周知,Java不仅提供了线程,也提供了线程池库给我们使用,那么今天来学学线程池的具体使用以及线程池基本实现原理分析. ThreadPoolExecutor ThreadPool ...

  5. 05 - Tomcat 线程池的配置与优化

    添加 Executor 在server.xml中的Service节点里面,增加executor节点,然后配置connector的executor属性,如下: <Executor name=&qu ...

  6. tomcat线程池

    tomcat线程池和普通的线程池设计上有所区别,下面主要来看看它是如何设计的 tomcat中线程池的创建 org.apache.tomcat.util.net.AbstractEndpoint#cre ...

  7. Tomcat线程池,更符合大家想象的可扩展线程池

    因由 说起线程池,大家可能受连接池的印象影响,天然的认为,它应该是一开始有core条线程,忙不过来了就扩展到max条线程,闲的时候又回落到core条线程,如果还有更高的高峰,就放进一个缓冲队列里缓冲一 ...

  8. Tomcat线程池配置

    简介  线程池作为提高程序处理数据能力的一种方案,应用非常广泛.大量的服务器都或多或少的使用到了线程池技术,不管是用Java还是C++实现,线程池都有如下的特点:线程池一般有三个重要参数: 最大线程数 ...

  9. tomcat 线程池

    web server允许的最大线程连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右. 1.编辑tomcat安装目录下的conf目录下的server. ...

  10. c++11 线程池学习笔记 (一) 任务队列

    学习内容来自一下地址 http://www.cnblogs.com/qicosmos/p/4772486.html github https://github.com/qicosmos/cosmos ...

随机推荐

  1. win10 使用idea 构建一个ssm的模板maven项目

    一.创建一个maven项目   1.1建立一个module作为web项目   File->New->project 选择maven,默认jdk,下面的列表什么都不选,next->输入 ...

  2. Linux之top命令分析

    第一行: top - 04:25:26 当前系统时间 up 3 min, 系统已经运行的时间(不间歇的运行) 1 user, 当前登录系统的用户数 load average: 0.01, 0.03, ...

  3. centos7下启动Django项目报错(sqlite错误)

    报错内容如下: [root@localhost project]# python3 manage.py runserver Watching for file changes with StatRel ...

  4. 霍夫变换原理及实现(Opencv C++)

    已知一幅图像中的n个点,假设我们希望找到这些点中位于直线上的子集.一种可能的解决方法是,首先找到由每对点确定的所有直线,然后寻找靠近特定直线的那些点的所有子集.这种方法涉及寻找n(n-1)/2~n2条 ...

  5. python获取豆瓣电影TOP250的所有电影的相关信息

    参考文档:https://weread.qq.com/web/reader/37132a705e2b2f37196c138k98f3284021498f137082c2e 说明:我才接触网络爬虫,在看 ...

  6. 探索Native Plugins:开启大模型的技能之门

    前言 上一章节我们了解了一下Semantic Kernnel中Plugins插件的概念以及学习了的 Semantic Kernel 模板插件的创建,本章节我们来学习 Native Plugins 原生 ...

  7. 使用 JMX-Exporter 监控 Kafka 和 Zookeeper

    JVM 默认会通过 JMX 的方式暴露基础指标,很多中间件也会通过 JMX 的方式暴露业务指标,比如 Kafka.Zookeeper.ActiveMQ.Cassandra.Spark.Tomcat.F ...

  8. koishi-跨平台、可扩展、高性能的机器人

    koishi 介绍 Koishi 是一个跨平台.可扩展.高性能的聊天机器人框架. 它的名字和图标设计来源于东方 Project 中的角色 古明地恋 (Komeiji Koishi).古明地恋是一个会做 ...

  9. Cursor是什么?基于ChatGPT代码编辑器的cursor如何使用?VS Code如何迁移到Cursor的步骤

    Cursor是什么 Cursor 是一个基于 Visual Studio Code(VS Code)技术构建的高级代码编辑器,专为提高编程效率并更深度地整合 AI 功能而设计.它不仅继承了 VS Co ...

  10. 什么是JDBC的最佳实践?

    a.数据库资源是非常昂贵的,用完了应该尽快关闭它.Connection,   Statement,    ResultSet等JDBC对象都有close方法,调用它就好了. b.养成在代码中显式关闭掉 ...