Tomcat 线程池

本文详细解析了Tomcat内部实现的线程池,包括如何创建、预启动核心线程,以及自定义的入队逻辑和任务执行计数管理。特别关注了其对最大线程数的限制和任务优先级的调度策略。

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();
    ...
}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值