Tomcat中线程池跟JDK的线程池有所不同,这里做个记录。
注:Tomcat源码为9.0.78版本的
Tomcat线程池构建方式
// 任务队列
TaskQueue taskqueue = new TaskQueue();
// 线程工厂
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
// 线程池,这里的ThreadPoolExecutor是tomcat自定义的,而非jdk中的
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
// 这个下面讨论
taskqueue.setParent( (ThreadPoolExecutor) executor);
- minSpareThreads = 5
- maxThreads = 200
- TaskQueue ,TaskQueue是Tomcat自定义的阻塞队列,继承自LinkedBlockingQueue,容量上限是Integer.MAX_VALUE,后面还会说到。
JDK线程池执行策略
这里附上jdk的ThreadPoolExecutor执行策略,对比来分析Tomcat的线程池有什么不同:
可以看到,如果采用的是无界队列,最大线程数将失去意义(最大线程数 > 核心线程数),超过核心线程数的任务会被直接添加到任务队列中。Tomcat是怎么解决这个问题的呢?看下Tomcat线程池的执行策略。
Tomcat线程池执行策略
private void executeInternal(Runnable command) {
if (command == null) {
throw new NullPointerException();
}
int c = ctl.get();
// 当前线程数小于核心线程数,创建线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) {
return;
}
c = ctl.get();
}
// 尝试添加到任务队列中
if (isRunning(c) && workQueue.offer(command)) {
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);
}
}
整体实现上跟jdk的线程池没什么区别,通过Tomcat线程池构建方式来看,这里的workQueue是Tomcat自定义实现的,重点关注下。
TaskQueue
public class TaskQueue extends LinkedBlockingQueue<Runnable> {
private transient volatile ThreadPoolExecutor parent = null;
public TaskQueue() {
super();
}
public void setParent(ThreadPoolExecutor tp) {
parent = tp;
}
@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.getPoolSizeNoLock() == parent.getMaximumPoolSize()) {
return super.offer(o);
}
//we have idle threads, just add it to the queue
// 执行到这里 最大线程数 > 当前线程数 > 核心线程数。如果提交的任务数 <= 当前线程数则说明存在空闲线程,则提交到任务队列,等待执行
if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) {
return super.offer(o);
}
//if we have less threads than maximum force creation of a new thread
// 执行到这,说明提交的任务数已经大于当前线程数,需要创建新的线程
if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {
return false;
}
//if we reached here, we need to add it to the queue
return super.offer(o);
}
}
- submittedCount:Tomcat自定义的ThreadPoolExecutor中使用该字段来标记当前已提交的任务数,提交加一,执行完减一。
- poolSizeNoLock:当前执行的线程数
Tomcat在构建线程池的时候,调用了taskqueue.setParent( (ThreadPoolExecutor) executor),参考Tomcat线程池执行策略中,在workQueue.offer()时会判断线程池的任务数量,来决定是创建线程还是入任务队列。整理tomcat的执行策略如下: