线程池参数
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
核心线程数
线程池核心线程数怎么设置?核心线程数代表的是系统的并发度,如何理解,例如:线程池设置核心线程数为8,平均处理一个任务的事件是1s,那么此时系统的QPS就是8×1/1 = 8,由此可见提高QPS的方法就是提高核心线程数,但是另外一个问题是,线程增加往往意味着cpu调度开销的增加,此时处理一个任务的事件可能会增加。对于计算密集型的任务,频繁的切换线程意味着CPU用在切换线程上的事件增加,而用于计算的时间会减少,此时系统的吞吐量会下降,所以对于此类任务,建议设置线程池的核心线程为CPU的核心数,最大线程也设置为CPU的核心数,保证所有的CPU计算资源都在进行业务计算;
而对于IO密集型的任务,CPU可能会有很长的空闲时间,因为有大量的IO等待,数据还未准备好,所以此时适当的增加线程数有助于提高并发度,提升吞吐量,对于这种场景,我们尽量使用压测的方法找到QPS的最大值,记下此时的线程数,可以将此数作为核心线程数,一般来说,如果用于计算的时间需要维持在50%,则线程数为cpu核心数*2
任务队列
任务队列,任务队里的核心点是缓冲,换句话说,CPU忙不过的任务先放入到任务队列中,任务队列的作用就是削峰填谷,让我们的系统可以有一定承受突发流量的能力,那么任务队列的大小该如何设置呢,对于被处理的任务,一般会有一个超时时间,比如说,一个请求最多等待5s,系统的QPS是1,每秒才能处理1个任务,这个时候任务队列的大小理论上来说应该等于 (5-1)/1 = 4,如果超过这个值,即使再增加任务队里的大小也毫无意义,因为最后一个请求的处理时间将超过5秒,而前面系统此时已经判定为操作超时失败了。
最大线程数
任务队列的作用是处理一些突发的流量,作为一种缓冲的作用,而设置比核心线程数大最大线程数也是为了处理流量的短时间内激增的一个方式,当任务队列满的时候,如果线程数量未到达最大的线程数,线程池会继续创建出线程来处理任务。
架构设计问题
我认为,正常流量下,系统的任务队列中不应该长时间存在大量的等待任务,如果出现这种问题,说明系统的处理能力已经出现瓶颈,此时应该考虑扩容。
系统处理任务的速度和任务产生的速度一致,如果此时产生突发流量,任务队列中将堆积一些任务,后续即使流量下降到正常水平,任务队列中的任务也将一直保持,如果突发流量的再产生几次,任务队列将会很快占满,后续请求将会全部失败。所以正常情况下,系统处理任务的速度应该是快于任务产生的速度,这样才能让系统承受一些短期的流量突发。
另外一个问题就是随着最大线程数的增加,对于下游系统的影响也不能忽视。