合理估算线程池大小

服务器性能IO优化 中发现一个估算公式:
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
可以得出一个结论:
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。


一个系统最快的部分是CPU,所以决定一个系统吞吐量上限的是CPU。增强CPU处理能力,可以提高系统吞吐量上限。但根据短板效应,真实的系统吞吐量并不能单纯根据CPU来计算。那要提高系统吞吐量,就需要从“系统短板”(比如网络延迟、IO)着手:

 * 尽量提高短板操作的并行化比率,比如多线程下载技术
 * 增强短板能力,比如用NIO替代IO

 

第一条可以联系到Amdahl定律,这条定律定义了串行系统并行化后的加速比计算公式:
加速比=优化前系统耗时 / 优化后系统耗时
加速比越大,表明系统并行化的优化效果越好。Addahl定律还给出了系统并行度、CPU数目和加速比的关系,加速比为Speedup,系统串行化比率(指串行执行代码所占比率)为F,CPU数目为N:
Speedup <= 1 / (F + (1-F)/N)
当N足够大时,串行化比率F越小,加速比Speedup越大。


是否使用线程池就一定比使用单线程高效呢?
答案是否定的,比如Redis就是单线程的,但它却非常高效,基本操作都能达到十万量级/s。从线程这个角度来看,部分原因在于:

 * 多线程带来线程上下文切换开销,单线程就没有这种开销
 * 锁

“Redis很快”更本质的原因在于:Redis基本都是内存操作,这种情况下单线程可以很高效地利用CPU。而多线程适用场景一般是:存在相当比例的IO和网络操作。
需要结合系统真实情况(比如是IO密集型或者是CPU密集型或者是纯内存操作)和硬件环境(CPU、内存、硬盘读写速度、网络状况等)来不断尝试以寻求一个符合实际的合理值。

 

参考:http://ifeve.com/how-to-calculate-threadpool-size/

### 如何设置合适的线程池队列大小线程池的设计中,合理设置队列大小是一个重要的环节。如果队列过大,则可能导致内存占用过高甚至引发 OutOfMemoryError;如果过小,则可能频繁触发拒绝策略,影响系统的吞吐量。 #### 队列类型的分类及其特点 Java 中常见的线程池队列类型有 `SynchronousQueue`、`ArrayBlockingQueue` 和 `LinkedBlockingQueue` 等[^1]。每种队列都有其适用场景: - **SynchronousQueue**: 不存储任务,生产者线程会一直阻塞直到消费者线程取走任务。适用于高并发场景下的短时间任务处理。 - **ArrayBlockingQueue**: 一个基于数组实现的有界队列,具有固定的容量上限。适合于需要控制队列长度并防止无限制增长的任务调度环境。 - **LinkedBlockingQueue**: 默认情况下是无界的队列(除非显式指定容量),容易导致内存泄漏风险。因此,在实际应用中通常建议为其设定合理的容量限制[^3]。 #### 计算合适队列大小的方法 为了找到最优解,可以考虑以下几个因素来进行估算: 1. **CPU 密集型任务** 对于 CPU 密集型操作而言,理想中的工作线程数量大约等于可用处理器核数加上一两个额外缓冲区位置即可满足需求。假设系统拥有 N 个逻辑核心,则推荐初始值设为 `(N + 1)` 或稍微多一点作为最大线程数目,并据此推导出适当规模的任务等待列表尺寸[^4]。 2. **I/O 密集型任务** I/O 绑定的操作往往涉及大量等待事件发生的时间片切换开销,所以应该增加更多的活动单元来弥补这部分损失效率的部分。经验法则表明,这类情形下可接受的工作进程总数可能是前者的好几倍——具体数值取决于磁盘读写速度或者网络延迟等因素的影响程度[^5]。 3. **综合考量硬件资源与业务特性** 结合具体的服务器配置信息(如 RAM 大小)、预期负载水平以及目标 SLA 要求等条件共同决定最终参数组合方案。例如某电商平台高峰期订单创建频率极高但单次耗时不长的话,那么就可以相应提高允许排队的数量以便更好地吸收瞬时流量高峰冲击力。 以下是通过编程方式动态调整线程池队列大小的一个例子: ```java public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100)); threadPoolStatus(executor,"改变之前"); ((ReWritedCapacityLinkedBlockingQueue)executor.getQueue()).setCapacity(200); threadPoolStatus(executor,"改变之后"); } private static void threadPoolStatus(ThreadPoolExecutor executor,String status){ System.out.println(status+" : "+executor.toString()); } ``` #### 动态监控机制的重要性 实时跟踪正在执行过程当中的各项指标数据对于及时发现问题所在至关重要。借助诸如 JMX 工具或者其他专门开发出来的框架库能够轻松获取到有关提交次数统计、完成比例百分比等方面的具体细节描述。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值