Tomcat源码解析之线程池的设计和实现

50 阅读4分钟

背景

前面我们了解到,Exector是包含在Service中,Service中关于Execute的配置和相关代码如下:

<Service name="Catalina">
<!-- 1. 属性说明
	name:Service的名称
-->

    <!--2. 一个或多个excecutors --> // 看这里
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->
</Service>    

Service中executors相关方法

/**
  * Adds a named executor to the service
  * @param ex Executor
  */
@Override
public void addExecutor(Executor ex) {
    synchronized (executors) {
        if (!executors.contains(ex)) {
            executors.add(ex);
            if (getState().isAvailable()) {
                try {
                    ex.start(); // 启动
                } catch (LifecycleException x) {
                    log.error(sm.getString("standardService.executor.start"), x);
                }
            }
        }
    }
}

/**
  * Retrieves all executors
  * @return Executor[]
  */
@Override
public Executor[] findExecutors() {
    synchronized (executors) {
        Executor[] arr = new Executor[executors.size()];
        executors.toArray(arr);
        return arr;
    }
}
/**
  * Retrieves executor by name, null if not found
  * @param executorName String
  * @return Executor
  */
@Override
public Executor getExecutor(String executorName) {
    synchronized (executors) {
        for (Executor executor: executors) {
            if (executorName.equals(executor.getName()))
                return executor;
        }
    }
    return null;
}

/**
  * Removes an executor from the service
  * @param ex Executor
  */
@Override
public void removeExecutor(Executor ex) {
    synchronized (executors) {
        if ( executors.remove(ex) && getState().isAvailable() ) {
            try {
                ex.stop(); // 停止
            } catch (LifecycleException e) {
                log.error(sm.getString("standardService.executor.stop"), e);
            }
        }
    }
}

和Server、Service实现一样,StandardThreadExecutor也是即成了生命周期接口LifecycleMBeanBase;然后实现Executor接口。

Execute接口设计

1.Tomcat希望将Executor也纳入Lifecycle生命周期管理,所以实现了Lifecycle接口

2.引入超时机制:当work queue满时,会等待指定的时间,如果超时将抛出RejectedExectionException,所以加了个void execute(Runnable command, long timeout, TimeUnit unit)方法。

package org.apache.catalina;

import java.util.concurrent.TimeUnit;

public interface Executor extends java.util.concurrent.Executor, Lifecycle {

    public String getName();

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the <code>Executor</code> implementation.
     * If no threads are available, it will be added to the work queue.
     * If the work queue is full, the system will wait for the specified
     * time until it throws a RejectedExecutionException
     *
     * @param command the runnable task
     * @param timeout the length of time to wait for the task to complete
     * @param unit    the units in which timeout is expressed
     *
     * @throws java.util.concurrent.RejectedExecutionException if this task
     * cannot be accepted for execution - the queue is full
     * @throws NullPointerException if command or unit is null
     *
     * @deprecated Unused. Will be removed in Tomcat 10.1.x onwards.
     */
    @Deprecated
    void execute(Runnable command, long timeout, TimeUnit unit);
}

StandardThreadExecutor的实现

Executor的所有实现都要支持以下属性

  • className:实现的类。实现必须实现 org.apache.catalina.Executor接口。此接口确保可以通过其name属性引用对象并实现Lifecycle,以便可以使用容器启动和停止对象。className的默认值org.apache.catalina.core.StandardThreadExecutor
  • name:用于server.xml中的其他位置引用此池的名称。该名称是必须的,且唯一的

StandardThreadExecutor属性

  • threadPriority:(int)执行程序中线程的线程优先级,默认为5(Thread.NORM_PRIORITY常量的值)
  • daemon:(boolean)线程是否应该是守护程序线程,默认是true
  • namePrefix:(字符串)执行程序创建的每个线程的名称前缀。单个线程的线程名称将是namePrefix + threadNumber
  • maxTheads:线程中活动线程的最大数量,默认为200
  • minSpareThreads:最小线程数始终保持活跃状态,默认25
  • maxIdleTime:空闲线程关闭之前的毫秒数
  • maxQueueSize:在我们拒绝之前可以排队等待执行的可运行任务的最大数量
  • prestartminSpareThreads:是否在启动Executor时启动minSpareThreads,默认为false
  • threadRenewalDelay:如果配置了ThreadLocalLeakPreventionListener,它将通知此执行程序有关已停止的上下文。上下文停止后,池中的线程将被更新。

Lifecycle模版方法

  • 先看下核心变量:

    private TaskQueue taskqueue = null; protected ThreadPoolExecutor executor = null;

  • initInternaldestroyInternal默认父类实现

    @Override protected void initInternal() throws LifecycleException { super.initInternal(); } @Override protected void destroyInternal() throws LifecycleException { super.destroyInternal(); }

  • startInternal方法

这个方法中中,我们不难看出,就是初始化taskqueue,同时构造ThreadPoolExecutor的实例,后面的StardardThreadExecutor的实现本质上通过ThreadPoolExecutor实现的。

/**
 * Start the component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected void startInternal() throws LifecycleException {

taskqueue = new TaskQueue(maxQueueSize);
TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
executor.setThreadRenewalDelay(threadRenewalDelay);
if (prestartminSpareThreads) {
    executor.prestartAllCoreThreads();
}
taskqueue.setParent(executor);

setState(LifecycleState.STARTING);
}
  • stopInternal方法

关闭线程池后置为null,方便GC回收

/**
     * Stop the component and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that needs to be reported
     */
    @Override
    protected void stopInternal() throws LifecycleException {

        setState(LifecycleState.STOPPING);
        if (executor != null) {
            executor.shutdownNow();
        }
        executor = null;
        taskqueue = null;
    }

核心executor方法

本质上就是调用ThreadPoolExecutor实例相关方法

@Override
    @Deprecated
    public void execute(Runnable command, long timeout, TimeUnit unit) {
        if (executor != null) {
            executor.execute(command,timeout,unit);
        } else {
            throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
        }
    }


    @Override
    public void execute(Runnable command) {
        if (executor != null) {
            // Note any RejectedExecutionException due to the use of TaskQueue
            // will be handled by the o.a.t.u.threads.ThreadPoolExecutor
            executor.execute(command);
        } else {
            throw new IllegalStateException(sm.getString("standardThreadExecutor.notStarted"));
        }
    }

动态调整线程池

我们还注意到StandardThreadExecutor还实现了ResizableExecutor,从名称上大概能推测出它是希望实现对线程池的动态调整,所以封装了该接口

public interface ResizableExecutor extends Executor {

    /**
     * Returns the current number of threads in the pool.
     *
     * @return the number of threads
     */
    public int getPoolSize();

    public int getMaxThreads();

    /**
     * Returns the approximate number of threads that are actively executing
     * tasks.
     *
     * @return the number of threads
     */
    public int getActiveCount();

    public boolean resizePool(int corePoolSize, int maximumPoolSize);

    public boolean resizeQueue(int capacity);

}

前三个方法比较简单,可以看下后两个方法

 @Override
    public boolean resizePool(int corePoolSize, int maximumPoolSize) {
        if (executor == null) {
            return false;
        }

        executor.setCorePoolSize(corePoolSize);
        executor.setMaximumPoolSize(maximumPoolSize);
        return true;
    }


    @Override
    public boolean resizeQueue(int capacity) {
        return false;
    }