线程池参数配置原则

 2022-09-19
原文地址:https://blog.51cto.com/splendors/5583497

线程池都经常用,但是具体的参数和具体的参数设置一定要知道,否会任务拒绝或者多线程上下文切换频繁;

高并发尽量不要用java提供的FixedThreadPool和SingleThreadExecutor ,直接用ThreadPoolExecutor自己定义参数,合理的配置参数。

通用的设置:
* 具体的参数设置需要根据几个值来决定
- tasks :希望每秒能执行的任务数
- taskcost:平均每个任务花费时间(包含cpu轮转的时间)
- responsetime:系统允许容忍的最大响应时间
* 做几个计算
- corePoolSize 核心处理线程
* threadcount = tasks/(1/taskcost) =tasks*taskcout 。corePoolSize设置应该大于最小值
* 根据8020原则,取最大值的80%
- queueCapacity = (coreSizePool/taskcost)*responsetime
* 计算可得 queueCapacity 。超过了的需要新开线程来执行
* 切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。
- maxPoolSize 最大线程数
* 计算可得 maxPoolSize =(max(tasks)- queueCapacity)/(1/taskcost)
* (最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数
- rejectedExecutionHandler:拒绝策略根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理
- keepAliveTime和allowCoreThreadTimeout,超时等待时间采用默认通常能满足

正常情况下,线程过多的时候会引发线程切换,会有CPU轮转的时间被浪费掉。

如果不知道额参数的话,将CPU的利用率最大化:

* CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。

* 混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。

* 对于IO型的任务的最佳线程数,有个公式可以计算

Nthreads = NCPU * UCPU * (1 + W/C)

其中:

* NCPU是处理器的核的数目

* UCPU是期望的CPU利用率(该值应该介于0和1之间)

* W/C是等待时间与计算时间的比率