Tomat组件研究之ThreadPool
前几天曾向大家承诺,要完成ThredPool,Tomcat SSl的文章,今天终于有时间可以写一点。Tomcat的ThradPool不同于Apache的Common ThradPool,Tomcat的ThreadPool是专门为Tomcat服务的,确切说是为Tomcat处理Http连接服务地。经过研究发现,Apache用了及其难懂而又隐晦的方法写了这几个ThreadPool类,虽然仅简单的几个类,但其难理解的程度却是惊人的。在理解之后看,里面确实又值得我们学习的东西,但也有好多无用的东西。看来我们也不要盲目崇拜Apache。废话少说,下面直入正题.
ThreadPool的Class图及整体结构:
一.每个类的说明:
1. org.apache.tomcat.util.threads.ThreadPool
线程池类,本类仅维护一定数量的线程处理对象,而把具体执行操作的任务委派给其他对象(ControlRunnable),Apache并没有把过多的功能交给这个类,而仅只是让这个类维护线程的创建,销毁,取出,归还等功能。下面我们看该类的代码:
public class ThreadPool {
(1) 线程池常量,对应的变量设置就不再列出了
//最大线程数
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;
//最大等待时间(1分钟)
public static final int WORK_WAIT_TIMEOUT = 60*1000;
(2) start方法
//对每个线程实例本方法仅被调用一次
public synchronized void start() {
//是否停止线程
stopThePool=false;
//当前生成线程的数量
currentThreadCount = 0;
//当前使用线程的数量
currentThreadsBusy = 0;
//如果当前设置的各个参数不正确,调整一下
adjustLimits();
//生成空的线程池
pool = new ControlRunnable[maxThreads];
//启动最小线数线程
openThreads(minSpareThreads);
//启动监视线程,监视线程池内部状态
monitor = new MonitorRunnable(this);
}
(3) openThreads方法
/**
* 启动指定数量(toOpen)的线程
* 这个方法很是奇怪,这个toOpen并不是本次打开的的线程数
* 而是本次要打开的和以前已经打开的线程数总和
*/
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;
}
到这里我们感觉Apache的做法好生奇怪,首先这个toOpen,还有一点,以前我们写连接池时,都时用List作为容器,一般有个当前的空闲线程数,但Apache偏偏用数组作为容器来存放线程,用数组就要维护每种线程(新的,使用的,空闲的)在数组中的下标,若用List这些问题就没了,我们只要get,add,remove一下就一切OK,非常方便。因为有了currentThreadsBusy,Apache的当前的空闲线程数就必须用currentThreadCount- currentThreadsBusy计算得来。这就时我们为什么会看到上面那个奇怪得小循环。但用数组到底有什么好处呢,还是Apache的人是猪头(靠,他们不可能是猪头)?,我们可能发现上面有个常量:
//最大线程数
public static final int MAX_THREADS = 200;
也就是说,默认最多池可以有200个线程,就是说有很多线程可能频繁地从池中
取,放线程,如果用List效率将大打折扣,因此才用了数组。
(4) findControlRunnable方法,取得一个可用线程
private ControlRunnable findControlRunnable() {
ControlRunnable c=null;
if ( stopThePool ) {
throw new IllegalStateException();
}
//从池中取一个可用的线程.
synchronized(this) {
//当前所有的线程都被使用
while (currentThreadsBusy == currentThreadCount) {
//当前的线程数量小于最大线程数
if (currentThreadCount < maxThreads) {
//生成一定数量(minSpareThreads)线程,这一点与
//我们做连接池时非常不一样,我们往往只生成一个新连
//接,看来Apache的东西真是值得我们学习
int toOpen = currentThreadCount +
minSpareThreads;
openThreads(toOpen);
} else {
logFull(log, currentThreadCount,
maxThreads);
//如果所有线程(已到最大数)都被使用,则等待其他线
//程释放.
try {
this.wait();
}catch(InterruptedException e) {
log.error("Unexpected exception", e);
}
// Pool was stopped. Get away of the pool.
if( stopThePool) {
break;
}
}
}
// Pool was stopped. Get away of the pool.
if(0 == currentThreadCount || stopThePool) {
throw new IllegalStateException();
}
int pos = currentThreadCount - currentThreadsBusy - 1;
//经过上面一番折腾,在线程池里终于又有了空闲线程,下面取数
//组里最后一个线程
c = pool[pos];
//释放当前线程池对该线程的引用
pool[pos] = null;
//当然,使用线程的数量也要加1
currentThreadsBusy++;
}
return c;
}
<