Executor元素配置(线程池之三)

前面介绍完Tomcat的工作线程池的概述,和对JDK线程池的改进思路和整体的流程;

本文通过配置,来看看Tomcat的工作线程池究竟开放了哪些配置。

Introduction

The Executor represents a thread pool that can be shared between components in Tomcat. Historically there has been a thread pool per connector created but this allows you to share a thread pool, between (primarily) connector but also other components when those get configured to support executors


Executor 元素的配置,就是Tomcat的线程池;

需要注意的是,该线程池在不同的Connector中是共享的,除此之外,一些其它的Tomcat后端组件也共享该线程池;

也就是说,如果你配置了Tomcat的线程池,那么它在Tomcat中用n处的地方;


The executor has to implement the org.apache.catalina.Executor interface.

The executor is a nested element to the Service element. And in order for it to be picked up by the connectors, the Executor element has to appear prior to the Connector element in server.xml


Executor 接口是需要实现的,通过className可以进行指定;

另外,Executor 元素只能嵌入在Service元素中;

通用属性:

Common Attributes

All implementations of Executor support the following attributes:

Attribute Description
className

The class of the implementation. The implementation has to implement the org.apache.catalina.Executor interface. This interface ensures that the object can be referenced through its name attribute and that implements Lifecycle, so that it can be started and stopped with the container. The default value for the className is org.apache.catalina.core.StandardThreadExecutor


StandardThreadExecutor 是Executor接口默认的实现, 当然你也可以实现自己的独有的线程池配置;

name

The name used to reference this pool in other places in server.xml. The name is required and must be unique.


唯一标识Tomcat的线程池的名字;

StandardThreadExecutor是Tomcat的标准的线程池实现,该类其实就是个配置服务类,其主要还是将配置传到到org.apache.tomcat.util.threads包中的核心类中进行,而这个包咱们前面主要分析的就是这个:


看看StandardThreadExecutor配置:

Standard Implementation

The default implementation supports the following attributes:

Attribute Description
threadPriority

(int) The thread priority for threads in the executor, the default is 5 (the value of the Thread.NORM_PRIORITY constant)


线程级别

daemon

(boolean) Whether the threads should be daemon threads or not, the default is true


线程是否是daemon线程

namePrefix

(String) The name prefix for each thread created by the executor. The thread name for an individual thread will be namePrefix+threadNumber


线程前缀,如果你不写的话,默认是catalina- ,你可以对其进行自定义

maxThreads

(int) The max number of active threads in this pool, default is 200


传入ThreadExecutor的最大的线程数

minSpareThreads

(int) The minimum number of threads always kept alive, default is 25


传入ThreadExecutor的最小空闲线程数

maxIdleTime

(int) The number of milliseconds before an idle thread shutsdown, unless the number of active threads are less or equal to minSpareThreads. Default value is 60000(1 minute)


传入ThreadExecutor的空闲超时时间

maxQueueSize

(int) The maximum number of runnable tasks that can queue up awaiting execution before we reject them. Default value is Integer.MAX_VALUE


传入ThreadExecutor的workqueue的队列长度

prestartminSpareThreads

(boolean) Whether minSpareThreads should be started when starting the Executor or not, the default is false


是否在最开始ThreadExecutor启动的时候,就先启动一些线程让其大小等于minSpareThreads

threadRenewalDelay

(long) If a ThreadLocalLeakPreventionListener is configured, it will notify this executor about stopped contexts. After a context is stopped, threads in the pool are renewed. To avoid renewing all threads at the same time, this option sets a delay between renewal of any 2 threads. The value is in ms, default value is 1000 ms. If value is negative, threads are not renewed.

这个theadRenewalDelay属性挺有意思,需要好好说说;

从头来说,ThreadLocalLeakPreventionListener 配置了,可以监听应用停止,我们前面知道会触发contextStopping方法;

而在contextStopping方法中,线程池中的空闲线程会重新干掉,然后再创建,其目的就是为了将ThreadLocal等缓存清空,减轻线程负担,这个代码我们说了多少次了;


但是,在这个时候,如果一股脑的一下子重新renew线程,cpu直接就蹭蹭的上来了,这种情况我们应该要匀的乎的让线程慢慢的都被renew;

而这个theadRenewalDelay的意思就是,每一次搞1个线程,然后下一次搞之前间隔多少时间,这个就是theadRenewalDelay的配置了;


我们回顾一下,前面TaskQueue的poll方法,poll方法是从工作线程队列中弹出任务,如果发现没有任务,说明现在这会“Tomcat挺空闲”啊,就有机会“整肃”一下队伍了,也就是Tomcat线程池中的线程,也就是执行前面说的这个renew操作:


stopCurrentThreadIfNeeded方法其实挺抽象的,该方法中没有出现thread.kill的字样,但看到最后是抛出异常,其实就是让任务失败,线程中断的意思;

里面的lastTimeThreadKillItSelf属性是记录每一次kill掉线程的时间戳;

其次,每一次是lastTimeThreadKillItSelf + theadRenewalDelay 与当前的时间进行比对,如果超出了theadRenewalDelay 的秒数,说明线程是在间隔进行kill的,通过这样的方式就可以让线程renew的压力小一些;

我们可以看到lastTimeThreadKillItSelf其使用CAS的原因了,compareAndSet,说明在该处存在高并发对这个变量的操作,当发现超过时间戳之后,在Tomcat中会有n个任务同时可能进行到这里;

因此这里又不能用互斥锁,索性用乐观锁的话,会大大提升效率,因为这里面的流转是非常快的,应该是乐观的,适当提升提升cpu是可以解决的,无需放弃时间片,因此用独占锁不太合适;


总结:

本文主要讲解了一下Executor的配置,其主要是StandardThreadExecutor来实现的, 其主要传递的是这些线程池的配置,传递给Tomcatorg.apache.tomcat.util.threads包中的核心类进行实现;

Executor的配置中,threadRenewalDelay属性实现较复杂,并且这里并发是很大的,使用了CAS乐观锁的机制;

本站公众号
   欢迎关注本站公众号,获取更多程序园信息
开发小院