【Tomcat】-Tomcat线程池与JDK线程池对比

本文分析了Tomcat线程池与JDK线程池的区别,重点讲解了Tomcat自定义的TaskQueue阻塞队列和执行策略,如何处理任务队列满和线程数量控制。

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的线程池有什么不同:
jdk线程池
可以看到,如果采用的是无界队列,最大线程数将失去意义(最大线程数 > 核心线程数),超过核心线程数的任务会被直接添加到任务队列中。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的执行策略如下:
Tomcat线程池执行策略

Tomcat 线程池 JDK 线程池存在多方面的区别: - **适用场景**:Tomcat 线程池更适合用于 Web 服务器,适用于 IO 密集型应用场景;而 JDK 线程池是通用的解决方案,对于 CPU 密集型任务,JDK 的`ThreadPoolExecutor`通用性灵活性更高一些[^1][^2]。 - **工作队列**:Tomcat 线程池的工作队列是`TaskQueue`,它是 Tomcat 自定义的,继承自 JDK 的`LinkedBlockingQueue`,容量上限是`Integer.MAX_VALUE`;JDK 线程池使用的是 JDK 自带的阻塞队列,例如`LinkedBlockingQueue`、`ArrayBlockingQueue`等[^3][^4]。 - **线程启动策略**:Tomcat 线程池在构造方法中会调用`prestartAllCoreThreads()`方法,启动所有核心线程;JDK 线程池默认不会启动所有核心线程,只有当有任务提交时才会根据情况创建线程[^5]。 - **任务处理策略**:在任务处理上,JDK 线程池在队列未满时,优先让任务排队;而 Tomcat 线程池在线程数未达到最大线程数之前,优先创建线程执行任务[^2]。 以下是 Tomcat 线程池 JDK 线程池创建的简单代码示例对比: #### Tomcat 线程池创建示例 ```java import org.apache.tomcat.util.threads.TaskQueue; import org.apache.tomcat.util.threads.TaskThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.ThreadPoolExecutor; // 模拟 Tomcat 线程池创建 public class TomcatThreadPoolExample { public static void main(String[] args) { TaskQueue taskqueue = new TaskQueue(); TaskThreadFactory tf = new TaskThreadFactory("Tomcat-exec-", true, Thread.NORM_PRIORITY); ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 200, 60, TimeUnit.SECONDS, taskqueue, tf); taskqueue.setParent(executor); } } ``` #### JDK 线程池创建示例 ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; // JDK 线程池创建示例 public class JDKThreadPoolExample { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值