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

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线程池与JDK线程池对比_第1张图片
可以看到,如果采用的是无界队列,最大线程数将失去意义(最大线程数 > 核心线程数),超过核心线程数的任务会被直接添加到任务队列中。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线程池对比_第2张图片

你可能感兴趣的:(tomcat,java)