tomcat线程池和普通的线程池设计上有所区别,下面主要来看看它是如何设计的

tomcat中线程池的创建

org.apache.tomcat.util.net.AbstractEndpoint#createExecutor

tomcat创建线程池
public void createExecutor() {
internalExecutor = true;
// 任务队列和普通的队列有所区别,后续分析
TaskQueue taskqueue = new TaskQueue();
// 线程工厂用于创建线程 本地项目name=http-nio-port-exec-序号
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
// 创建线程池,注意这个ThreadPoolExecutor和java.util.concurrent包下的ThreadPoolExecutor有所区别
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
// 给任务队列设置线程池,用于后续任务来了判断是创建线程执行还是将线程添加到任务队列
taskqueue.setParent( (ThreadPoolExecutor) executor);
}

tomcat的ThreadPoolExecutor

tomcat的ThreadPoolExecutor实际上继承了java包的ThreadPoolExecutor再其上定制了一些功能

submittedCount:记录了线程池中正有多少线程在执行任务(还没执行完)

lastContextStoppedTime:记录上次上下文停止的时间

lastTimeThreadKilledItself:记录线程上一次为防止内存泄漏自我kill的时间

构造方法:调用了父类ThreadPoolExecutor,同时调用了prestartAllCoreThreads方法,再完成线程池的创建后预热核心线程,使得任务到来时能够直接执行任务,不用再花时间去创建线程,提高了效率。

execute方法:执行executor方法时首先将submittedCount加1,再调用父类的executor方法执行任务。若抛出RejectedExecutionException异常则再回尝试将任务添加到任务队列汇中

afterExecute:重写父类方法,任务执行完成后调用afterExecute钩子方法将submittedCount减1,再尝试停止线程

contextStopping:若容器上下文停止,则会记录lastContextStoppedTime为当前时间并中断正在运行的线程。调用currentThreadShouldBeStopped方法的时候会判断线程TaskThread创建的时间是否在lastContextStoppedTime之前,表示当前线程是在上一个上下文运行期间创建,则会尝试kill线程

public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager
.getManager("org.apache.tomcat.util.threads.res"); /**
* The number of tasks submitted but not yet finished. This includes tasks
* in the queue and tasks that have been handed to a worker thread but the
* latter did not start executing the task yet.
* This number is always greater or equal to {@link #getActiveCount()}.
*/
private final AtomicInteger submittedCount = new AtomicInteger(0);
private final AtomicLong lastContextStoppedTime = new AtomicLong(0L); /**
* Most recent time in ms when a thread decided to kill itself to avoid
* potential memory leaks. Useful to throttle the rate of renewals of
* threads.
*/
private final AtomicLong lastTimeThreadKilledItself = new AtomicLong(0L); /**
* Delay in ms between 2 threads being renewed. If negative, do not renew threads.
*/
private long threadRenewalDelay = Constants.DEFAULT_THREAD_RENEWAL_DELAY; public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
prestartAllCoreThreads();
} public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
prestartAllCoreThreads();
} public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectHandler());
prestartAllCoreThreads();
} public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new RejectHandler());
prestartAllCoreThreads();
} public long getThreadRenewalDelay() {
return threadRenewalDelay;
} public void setThreadRenewalDelay(long threadRenewalDelay) {
this.threadRenewalDelay = threadRenewalDelay;
} @Override
protected void afterExecute(Runnable r, Throwable t) {
submittedCount.decrementAndGet(); if (t == null) {
stopCurrentThreadIfNeeded();
}
} /**
* If the current thread was started before the last time when a context was
* stopped, an exception is thrown so that the current thread is stopped.
*/
protected void stopCurrentThreadIfNeeded() {
if (currentThreadShouldBeStopped()) {
long lastTime = lastTimeThreadKilledItself.longValue();
if (lastTime + threadRenewalDelay < System.currentTimeMillis()) {
if (lastTimeThreadKilledItself.compareAndSet(lastTime,
System.currentTimeMillis() + 1)) {
// OK, it's really time to dispose of this thread final String msg = sm.getString(
"threadPoolExecutor.threadStoppedToAvoidPotentialLeak",
Thread.currentThread().getName()); throw new StopPooledThreadException(msg);
}
}
}
} protected boolean currentThreadShouldBeStopped() {
if (threadRenewalDelay >= 0
&& Thread.currentThread() instanceof TaskThread) {
TaskThread currentTaskThread = (TaskThread) Thread.currentThread();
if (currentTaskThread.getCreationTime() <
this.lastContextStoppedTime.longValue()) {
return true;
}
}
return false;
} public int getSubmittedCount() {
return submittedCount.get();
} /**
* {@inheritDoc}
*/
@Override
public void execute(Runnable command) {
execute(command,0,TimeUnit.MILLISECONDS);
} /**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the <code>Executor</code> implementation.
* If no threads are available, it will be added to the work queue.
* If the work queue is full, the system will wait for the specified
* time and it throw a RejectedExecutionException if the queue is still
* full after that.
*
* @param command the runnable task
* @param timeout A timeout for the completion of the task
* @param unit The timeout time unit
* @throws RejectedExecutionException if this task cannot be
* accepted for execution - the queue is full
* @throws NullPointerException if command or unit is null
*/
public void execute(Runnable command, long timeout, TimeUnit unit) {
submittedCount.incrementAndGet();
try {
super.execute(command);
} catch (RejectedExecutionException rx) {
if (super.getQueue() instanceof TaskQueue) {
final TaskQueue queue = (TaskQueue)super.getQueue();
try {
if (!queue.force(command, timeout, unit)) {
submittedCount.decrementAndGet();
throw new RejectedExecutionException("Queue capacity is full.");
}
} catch (InterruptedException x) {
submittedCount.decrementAndGet();
throw new RejectedExecutionException(x);
}
} else {
submittedCount.decrementAndGet();
throw rx;
} }
} public void contextStopping() {
this.lastContextStoppedTime.set(System.currentTimeMillis()); // save the current pool parameters to restore them later
int savedCorePoolSize = this.getCorePoolSize();
TaskQueue taskQueue =
getQueue() instanceof TaskQueue ? (TaskQueue) getQueue() : null;
if (taskQueue != null) {
// note by slaurent : quite oddly threadPoolExecutor.setCorePoolSize
// checks that queue.remainingCapacity()==0. I did not understand
// why, but to get the intended effect of waking up idle threads, I
// temporarily fake this condition.
taskQueue.setForcedRemainingCapacity(Integer.valueOf(0));
} // setCorePoolSize(0) wakes idle threads
this.setCorePoolSize(0); // TaskQueue.take() takes care of timing out, so that we are sure that
// all threads of the pool are renewed in a limited time, something like
// (threadKeepAlive + longest request time) if (taskQueue != null) {
// ok, restore the state of the queue and pool
taskQueue.setForcedRemainingCapacity(null);
}
this.setCorePoolSize(savedCorePoolSize);
} private static class RejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r,
java.util.concurrent.ThreadPoolExecutor executor) {
throw new RejectedExecutionException();
} } }

tomcat的TaskQueue

首先简单翻一下注释,为线程池运行专门设计的队列,使用该队列线程池如果有空闲队列则会创建线程池执行任务而不是将任务放到任务队列中。

它继承了LinkedBlockingQueue无界队列,容量为Integer.MAX_VALUE。

在execute方法中可以看到,当线程池线程大于核心数量的时候,会执行任务队列的offer方法,下来来分析下TaskQueue的offer方法:

1.若parent为空也就是未给队列设置线程池,则调用父类offer方法,将任务添加到队列中

2.线程池当前线程数量等于线程池最大数量,无法添加更多的线程,调用父类offer方法,将任务添加到队列中

3.线程池正在执行任务的线程数量小于等于线程池已有的线程数量,说明当前线程池有空闲线程,调用父类offer方法,将任务添加到队列中,等待线程从队列中取任务运行

4.线程池线程数量小于线程池最大数量说明还可以增加线程,返回false,运行addWorker(command,false)向线程池添加非核心线程运行任务

5.都不满足,调用父类offer方法,将任务添加到队列中

从上面的分析我们可以看到该任务队列TaskQueue和普通的任务队列不一样,当线程池的线程数量小于最大线程数量时,任务不会添加到任务队列中,而是会添加非核心线程来运行任务,当线程池线程数量达到最大数量时,才会将任务添加到任务队列中

当然TaskQueue也有force方法直接调用父类offer方法将任务添加到任务队列中

/**
* 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 wont be able to force items onto the queue
* itself.
*/
public class TaskQueue extends LinkedBlockingQueue<Runnable> { private static final long serialVersionUID = 1L; private transient volatile ThreadPoolExecutor parent = null; // No need to be volatile. This is written and read in a single thread
// (when stopping a context and firing the listeners)
private Integer forcedRemainingCapacity = null; public TaskQueue() {
super();
} public TaskQueue(int capacity) {
super(capacity);
} public TaskQueue(Collection<? extends Runnable> c) {
super(c);
} public void setParent(ThreadPoolExecutor tp) {
parent = tp;
} public boolean force(Runnable o) {
if (parent == null || parent.isShutdown()) throw new RejectedExecutionException("Executor not running, can't force a command into the queue");
return super.offer(o); //forces the item onto the queue, to be used if the task is rejected
} public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
if (parent == null || parent.isShutdown()) throw new RejectedExecutionException("Executor not running, can't force a command into the queue");
return super.offer(o,timeout,unit); //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);
} @Override
public Runnable poll(long timeout, TimeUnit unit)
throws InterruptedException {
Runnable runnable = super.poll(timeout, unit);
if (runnable == null && parent != null) {
// the poll timed out, it gives an opportunity to stop the current
// thread if needed to avoid memory leaks.
parent.stopCurrentThreadIfNeeded();
}
return runnable;
} @Override
public Runnable take() throws InterruptedException {
if (parent != null && parent.currentThreadShouldBeStopped()) {
return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS),
TimeUnit.MILLISECONDS);
// yes, this may return null (in case of timeout) which normally
// does not occur with take()
// but the ThreadPoolExecutor implementation allows this
}
return super.take();
} @Override
public int remainingCapacity() {
if (forcedRemainingCapacity != null) {
// ThreadPoolExecutor.setCorePoolSize checks that
// remainingCapacity==0 to allow to interrupt idle threads
// I don't see why, but this hack allows to conform to this
// "requirement"
return forcedRemainingCapacity.intValue();
}
return super.remainingCapacity();
} public void setForcedRemainingCapacity(Integer forcedRemainingCapacity) {
this.forcedRemainingCapacity = forcedRemainingCapacity;
} }

当有请求来时,从socket获取到可读事件并将socket封装成一个任务(任务主要是解析请求然后下发到servlet执行),然后调用线程池的execute方法

    /**
* Process the given SocketWrapper with the given status. Used to trigger
* processing as if the Poller (for those endpoints that have one)
* selected the socket.
*
* @param socketWrapper The socket wrapper to process
* @param event The socket event to be processed
* @param dispatch Should the processing be performed on a new
* container thread
*
* @return if processing was triggered successfully
*/
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}

tomcat线程池的更多相关文章

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

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

  2. Tomcat线程池的深入理解

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

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

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

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

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

  5. Tomcat线程池配置

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

  6. tomcat 线程池

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

  7. 如何计算tomcat线程池大小?

    背景 在我们的日常开发中都涉及到使用tomcat做为服务器,但是我们该设置多大的线程池呢?以及根据什么原则来设计这个线程池呢? 接下来,我将介绍本人是怎么设计以及计算的. 目标 确定tomcat服务器 ...

  8. Tomcat 线程池配置

    线程池 Executor代表了一个线程池,可以在Tomcat组件之间共享.使用线程池的好处在于减少了创建销毁线程的相关消耗,而且可以提高线程的使用效率.要想使用线程池,首先需要在 Service标签中 ...

  9. Tomcat线程池实现

    目前市场上常用的开源Java Web容器有Tomcat.Resin和Jetty.其中Resin从V3.0后需要购买才能用于商业目的,而其他两种则是纯开源的.可以分别从他们的网站上下载最新的二进制包和源 ...

随机推荐

  1. python字符编码与文件操作

    目录 字符编码 字符编码是什么 字符编码的发展史 字符编码实际应用 编码与解码 乱码问题 python解释器层面 文件操作 文件操作简介 文件的内置方法 文件的读写模式 文件的操作模式 作业 答案 第 ...

  2. Java 接口返回值集合防止空指针

    接口 返回值为一个集合 public interface UserSearchService{ List<User> listUser(); } 接口实现 public List<U ...

  3. Citrix Virtual Apps and Desktops 7 2203 LTSR虚拟云桌面单机教程

    哈喽大家好,欢迎来到虚拟化时代君(XNHCYL). 大家好,我是虚拟化时代君,一位潜心于互联网的技术宅男.这里每天为你分享各种你感兴趣的技术.教程.软件.资源.福利--(每天更新不间断) 一 .主要内 ...

  4. PowerShell 定时刷新查看文件内容

    get-content .\1.txt -ReadCount 0 -Tail 5 -Wait

  5. NCF 的Azure Cosmos DB 演示案例

    简介 NCF想必看过我之前发的NCF的文章的同学们都已经很熟悉了 今天我们要来聊一聊的是NCF遇到Azure Cosmos DB后会碰撞出什么样的火花,让我们一起往下看 我们先来说说什么是Azure ...

  6. BUUCTF-被偷走的文件

    被偷走的文件 这题刚开始还以为是单纯的流量题,看流量半天也没发现什么异常. 因为是文件传输过程的,所以我们看到ftp的流量就过滤下看看即可. 在第三个包发现flag.rar存在. 一开始我觉得没啥,后 ...

  7. SAP string 转 number 类型

    try.          cl_fdt_calculation=>convert_string_to_number( exporting  iv_text   = conv #( lwa_at ...

  8. k8s动态存储管理GlusterFS

    1. 在node上安装Gluster客户端(Heketi要求GlusterFS集群至少有三个节点) 删除master标签 kubectl taint nodes --all node-role.kub ...

  9. UiPath鼠标操作图像的介绍和使用

    一.鼠标(mouse)操作的介绍 模拟用户使用鼠标操作的一种行为,例如单击,双击,悬浮.根据作用对象的不同我们可以分为对元素的操作.对文本的操作和对图像的操作 二.鼠标对图像的操作在UiPath中的使 ...

  10. 从20s优化到500ms,我用了这三招

    前言 接口性能问题,对于从事后端开发的同学来说,是一个绕不开的话题.想要优化一个接口的性能,需要从多个方面着手. 其实,我之前也写过一篇接口性能优化相关的文章<聊聊接口性能优化的11个小技巧&g ...