tomcat源码学习--线程池
写个前言:
通常大家知道tomcat的线程池是可以配置的,去找到conf下面的的server.xml就可以搞定,先配置指定参数
<Servicename="Catalina">
<!--Theconnectorscanuseasharedexecutor,youcandefineoneormorenamedthreadpools-->
<!--
<Executorname="tomcatThreadPool"namePrefix="catalina-exec-"
maxThreads="150"minSpareThreads="4"/>
-->
在配置连接器使用它,注释中都给出了例子
<Connectorport="8080"protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"/>
<!--A"Connector"usingthesharedthreadpool-->
<!--
<Connectorexecutor="tomcatThreadPool"
port="8080"protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"/>
-->
但现在不是想说这个,是想看看tomcat源码里的线程池,只看配置文件没啥意义,不好玩
开始->>
首先看tomcat的源码,ThreadPool里面的成员变量
private static Log log = LogFactory.getLog(ThreadPool.class); private static StringManager sm = StringManager.getManager("org.apache.tomcat.util.threads.res"); private static boolean logfull=true; /* * Default values ... */ public static final int MAX_THREADS = 200; public static final int MAX_THREADS_MIN = 10; public static final int MAX_SPARE_THREADS = 50; public static final int MIN_SPARE_THREADS = 4; public static final int WORK_WAIT_TIMEOUT = 60*1000; /* * Where the threads are held. */ protected ControlRunnable[] pool = null; /* * A monitor thread that monitors the pool for idel threads. */ protected MonitorRunnable monitor; /* * Max number of threads that you can open in the pool. */ protected int maxThreads; /* * Min number of idel threads that you can leave in the pool. */ protected int minSpareThreads; /* * Max number of idel threads that you can leave in the pool. */ protected int maxSpareThreads; /* * Number of threads in the pool. */ protected int currentThreadCount; /* * Number of busy threads in the pool. */ protected int currentThreadsBusy; /* * Flag that the pool should terminate all the threads and stop. */ protected boolean stopThePool; /* Flag to control if the main thread is 'daemon' */ protected boolean isDaemon=true; /** The threads that are part of the pool. * Key is Thread, value is the ControlRunnable */ protected Hashtable threads=new Hashtable(); protected Vector listeners=new Vector(); /** Name of the threadpool */ protected String name = "TP"; /** * Sequence. */ protected int sequence = 1; /** * Thread priority. */ protected int threadPriority = Thread.NORM_PRIORITY; /** * Constructor. */ public ThreadPool() { maxThreads = MAX_THREADS; maxSpareThreads = MAX_SPARE_THREADS; minSpareThreads = MIN_SPARE_THREADS; currentThreadCount = 0; currentThreadsBusy = 0; stopThePool = false; }
构造函数里面默认指定了线程池配置的东西,这里的每个参数就不说了,tomcat用在这个类里面即对线程池用到的参数使用变量+常量形式让这个可配置型增加,也好理解修改
构造线程池的策略是什么呢?一个Hashtable来键值对存储
/**Thethreadsthatarepartofthepool.
*KeyisThread,valueistheControlRunnable
*/
protectedHashtablethreads=newHashtable();
publicvoidaddThread(Threadt,ControlRunnablecr){
threads.put(t,cr);
for(inti=0;i<listeners.size();i++){
ThreadPoolListenertpl=(ThreadPoolListener)listeners.elementAt(i);
tpl.threadStart(this,t);
}
}
publicvoidremoveThread(Threadt){
threads.remove(t);
for(inti=0;i<listeners.size();i++){
ThreadPoolListenertpl=(ThreadPoolListener)listeners.elementAt(i);
tpl.threadEnd(this,t);
}
}
里面的key经过了多层的包装,他里面封装了很多信息ThreadWithAttributes,这一切都是通过里面的内部类ControlRunnable来实现的,内部类真是个好东西,好多复杂的东西都写在里面,是不是业务逻辑复杂,抽取成方法也不好时,可以考虑抽取内部类
线程池启动时,做一些init工作,主要是把poolmonitor给初始化,adjustLimits()是一个防止你配置错误的validate,如果错误他会有些策略,有兴趣可以看下。完成这一切后。怎么处理线程呢?在newC的时候就
public synchronized void start() { stopThePool=false; currentThreadCount = 0; currentThreadsBusy = 0; adjustLimits(); pool = new ControlRunnable[maxThreads]; /**这个内部类的构造函数 ControlRunnable(ThreadPool p) { toRun = null; shouldTerminate = false; shouldRun = false; this.p = p; t = new ThreadWithAttributes(p, this); t.setDaemon(true); t.setName(p.getName() + "-Processor" + p.incSequence()); t.setPriority(p.getThreadPriority()); p.addThread( t, this ); noThData=true; /**这个start听复杂的,看这运行key的,但可以是ThreadWithAttributes,他继承自Thread,那么它部是空吗?不是在构造方法里面 super(r);这个方法以为着运行目标变成当前类ControlRunnable*/ t.start(); } 创建的数组也并没初始化这个构造函数,有这样一个方法,刚开始直接给你构造好最小个线程 protected void openThreads(int toOpen) { if(toOpen > maxThreads) { toOpen = maxThreads; } for(int i = currentThreadCount ; i < toOpen ; i++) { /**居然还是倒着往里面赋值**/ pool[i - currentThreadsBusy] = new ControlRunnable(this); } currentThreadCount = toOpen; } **/ openThreads(minSpareThreads); if (maxSpareThreads < maxThreads) { monitor = new MonitorRunnable(this); } }
初始化一切搞定,当线程调用run,那么会去查找相关已经初始化的线程,再取出来,对应去运行
public void run(Runnable r) { ControlRunnable c = findControlRunnable(); c.runIt(r); }
那么着就完了吗?tomcat还提供了一个接口,你要使用线程池只需继承它是想相关的东西
/** Implemented if you want to run a piece of code inside a thread pool. */ public interface ThreadPoolRunnable { // XXX use notes or a hashtable-like // Important: ThreadData in JDK1.2 is implemented as a Hashtable( Thread -> object ), // expensive. /** Called when this object is first loaded in the thread pool. * Important: all workers in a pool must be of the same type, * otherwise the mechanism becomes more complex. */ public Object[] getInitData(); /** This method will be executed in one of the pool's threads. The * thread will be returned to the pool. */ public void runIt(Object thData[]); }
那么tomcat又在何处使用它了,利用eclipse工具,找到调用类PoolTcpEndpoint,里面的线程是LeaderFollowerWorkerThread还实现了ThreadPoolRunnable,感兴趣可以自己去看看
综上所述,它实现是怎样的,先初始化一定数量的线程,他们都先睡着,线程池runIt(listener)传入线程,传入后就不notify然后就shouldRun=true,然后就调用传进来的线程。但是包装的东西好多啊,控制的东西验证的东西也好多,造成了这个类好大。