1.工作流程

1.不会初始化corePoolSize个线程,有任务来了才创建工作线程;

2.当核心线程满了之后不会立即扩容线程池,而是把任务堆积到工作队列中;

3.当工作队列满了以后扩容线程池,一直到线程个数达到maximumPoolSize为止;

4.如果队列已满且达到了最大线程后还有任务进来,按照拒绝策略处理;

5.当线程数大于核心线程数时,线程等待keepAliveTime后还是没有任务需要处理时,收缩线程到核心线程数;

2.参数设置

tasks,程序每秒需要处理的最大任务数量(假设系统每秒任务数为100~1000)

tasktime,单线程处理一个任务所需要的时间(假设每个任务耗时0.1秒)

responsetime,系统允许任务最大的响应时间(假设每个任务的响应时间不得超过2秒)

1.corePoolSize

每个任务需要耗时 taskTime

单线程每秒可以处理的任务数 = 1 / taskTime

系统每秒的任务数为tasks,需要的线程数为 tasks/(1/taskTime) 即 tasks * taskTime

例子: 系统每秒任务数为100到1000之间,每个任务耗时0.1秒,则需要100x0.1至1000x0.1,即10到100个线程。那么corePoolSize应该设置为大于10。

具体数字需要参考系统平均QPS, 峰值QPS

最好根据8020原则,即80%情况下系统每秒任务数,若系统80%的情况下任务数小于200,最多时为1000,则corePoolSize可设置为20

2.QueueCapacity

队列长度 = (corePoolSize / tasktime ) * responseTime (核心线程数 / 单个任务耗时) * 单任务最大响应时间 = (核心线程每秒可以处理的任务数) * 单任务最大响应时间

例子: (20/0.1)*2=400,即队列长度可设置为400

如果队列长度过长,则任务响应时间过长

3.maxPoolSize

当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200),即60个线程,可将maxPoolSize设置为60。

4.keepAliveTime

线程数量只增加不减少也不行。当负载降低时,可减少线程数量,如果一个线程空闲时间达到keepAliveTiime,该线程就退出。默认情况下线程池最少会保持corePoolSize个线程。keepAliveTiime设定值可根据任务峰值持续时间来设定。

以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况,比如,当线程数量达到50时,CPU达到100%,则将maxPoolSize设置为60也不合适,此时若系统负载长时间维持在每秒1000个任务,则超出线程池处理能力,应设法降低每个任务的处理时间(tasktime)。

参考

线程池参数的合理设置

Java 线程池

一次线程池引发的线上故障分析

Java线程池实现原理及其在美团业务中的实践

如何合理地估算线程池大小