前提

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. vue2遇到的一些错误

    一.VUE中的VUEX如何调用modules里面的mutations和state   ...mapMutations("workflow",['setApproverConfig' ...

  2. 【ESP32】制作 Wi-fi 音箱(HTTP + I2S 协议)

    用 Wifi 来传输音频数据,会比蓝牙更好.使用蓝牙方式,不管你用什么协议,都会对数据重新编码,说人话就是有损音质,虽然不至于全损.而使用 Wifi 就可以将 PCM 数据直接传输,无需再编码和压缩. ...

  3. 企业级nginx内核优化篇

    1. cpu核心配置 方法1: worker_processes auto; 自动调用[推荐] 方法2: worker_processes 4; 手工配置 检查CPU核心:yum install nu ...

  4. Go的接口与多态

    什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他的变量是一堆方法. 一个简单的案例: r,_ := http.Get("http://www.baidu.com") i ...

  5. Centos7安装Nginx教程,一步安装http和https

    nginx是一款轻量级web服务器,主要有负载均衡和反向代理的特性. 安装准备 nginx一些模块需要依赖lib库,所以先安装lib库,执行以下命令: [root@localhost local]# ...

  6. 动态生成的 select option 无法选中,设置值

    使用jQuery的 .val('22') 给select 设置值时不生效. 原因:select是动态生成的,在DOM还没生成完之前就调用了.val('22'). 解决方法:动态生成的ajax请求改成同 ...

  7. json LocalDateTime转对象

    json LocalDateTime转对象 feign.codec.DecodeException: JSON parse error: Can not deserialize instance of ...

  8. 基于SDF的光照效果

    基于SDF的光照效果 好久没写博客了,怠惰了,就当爬了一步 原神二次元风格面部渲染 效果 Show me the code Shader "Unlit/SDF" { Propert ...

  9. Linux实时查看Java接口数据

    1.Linux实时查看Java接口数据的方法 在Linux系统中实时查看Java接口数据通常涉及几个步骤: (1)编写Java应用程序:首先,我们需要有一个Java应用程序,它暴露了一个或多个HTTP ...

  10. python logging去掉selenium大量的日志

    问题 二次封装logging模块,设置级别为DEBUG,默认所有级别的日志都可以收集到:在发起ui自动化,打开浏览器输入网址,进行页面操作时,打印了大量的connectionpool.remote_c ...