线程场景
- IO密集型任务:
任务类型主要是IO操作,CPU利用率低. - CPU密集型任务:
任务类型主要是计算工作,响应快,CPU会一直工作,其利用率也很高. - 混合型任务:
任务包含了IO操作和逻辑运算,由于IO操作的成本通常比逻辑操作的高出非常多.所以此类型任务,多数情况也是CPU利用率不高.
如何根据不同的场景构建合适的线程池
下面是基础定义:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
IO密集型任务
由于CPU使用率不高,且每个人物CPU工作时间较短,可以增加线程池中工作的线程,提高执行效率
- corePoolSize为CPU核心线程数2倍
- corePoolSize=maximumPoolSize,保证优先使用扩展线程的方式分配任务,而非进入阻塞队列
- workQueue必须使用有界队列
CPU密集型任务
CPU密集型任务,单个任务需要占用CPU较长的工作时间,如果线程池中工作的线程过多,会导致CPU更频繁的切换任务,这种时间消耗会使任务效率下降.所以定的时候,线程数等于CPU核心数就行.
- corePoolSize为CPU核心线程数
- corePoolSize=maximumPoolSize,保证优先使用扩展线程的方式分配任务,而非进入阻塞队列
- workQueue必须使用有界队列
混合型任务
混合型任务可以通过公式计算出最佳线程数:
最佳线程数 = ((线程等待时间+线程CPU时间) / 线程CPU时间) * CPU核数
或:
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1) * CPU核数
例如:WEB应用的HTTP请求,当请求到达后端时,任务的核心流程主要有两部分组成:
- 业务代码的执行,通常耗时较短(假定全程需要100ms)
- RPC,DB等IO操作,CPU几乎不工作,只需要等待资源(假定资源获取时cpu需要等待500ms)
- CPU物理核心为8core
此时理想线程数为: (500 + 100) / 100 * 8 = 48
- PoorSize:
PoorSize所指的既是线程池中最佳的活动的线程数.由于poorSize是由线程池的维护,是在实时变动的,所以在配置corePoolSize,maximumPoolSize两个参数的时候,至少要保证poorSize数量.
根据线程池维护和创建规则(前文有介绍),可以设置成:
corePoolSize=maximumPoolSize=48
corePoolSize: 核心线程池大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。
maximumPoolSize: 线程池中允许创建的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数(poolSize)小于maximumPoolSize,那么会创建新的线程来执行任务。
poorSize: 线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。
小结
线程池参数的定义对程序运行多线程效率有很大的影响,根据不同的使用场景,配置合适的线程池是十分重要的.
本文由 momoker 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Apr 24,2023