Tomcat 自己实现了一个线程池位于 org.apache.tomcat.util.threads 包下,继承自 JUC 的线程池:
public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {
Tomcat 会创建这个线程池:
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
// getMinSpareThreads() 默认10,可以通过server.tomcat.threads.max设置
// getMaxThreads() 默认200,可以通过server.tomcat.threads.min-spare设置
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
提前启动核心线程
构造方法中,调用了 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();
}
自定义入队逻辑
这里的阻塞队列使用的是 TaskQueue,继承自 LinkedBlockingQueue,其入队逻辑如下:
@Override
public boolean offer(Runnable o) {
// parent即线程池
if (parent==null) return super.offer(o);
// tomcat线程池会在构造方法中进行核心线程的创建,所以不存在线程数小于核心线程数的情况
// 这里就是判断线程数等于最大线程数才入队
if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
// 正在处理的任务数量小于等于当前线程数,即还能有空闲线程,此时入队
if (parent.getSubmittedCount()<=(parent.getPoolSize())) return super.offer(o);
// 线程数小于最大线程数时,入队失败,会去创建线程
if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
return super.offer(o);
}
Tomcat 的线程池,由于核心线程预先创建完成,所以当任务执行时,会直接入队的,它这里就是多了判断,只有在空闲线程充足时才会入队,否则会去创建线程直到最大线程数。
正在执行任务数量
Tomcat 继承的线程池,有一个 submittedCount 变量用来记录当前提交并且未执行完成的任务数量:
private final AtomicInteger submittedCount = new AtomicInteger(0);
在执行任务时就会加 1:
public void execute(Runnable command, long timeout, TimeUnit unit) {
submittedCount.incrementAndGet();
...
}
减 1 的时机则是利用了 ThreadPoolExecutor 提供的扩展方法 afterExecute:
inal void runWorker(Worker w) {
...
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
...
} finally {
// 任务结束后调用
afterExecute(task, thrown);
}
...
}
Tomcat 线程池重写了该方法:
@Override
protected void afterExecute(Runnable r, Throwable t) {
submittedCount.decrementAndGet();
...
}
本文详细解析了Tomcat内部实现的线程池,包括如何创建、预启动核心线程,以及自定义的入队逻辑和任务执行计数管理。特别关注了其对最大线程数的限制和任务优先级的调度策略。
1205





