原理
多线程的线程池可以提高程序的运行性能,这是毋庸置疑的,但是如何设置线程池的大小,以前一直是靠猜,或者根据经验配置,这两天对系统的线程池进行优化配置时,找到了如何合理地估算线程池大小这篇文章,在这里做个记录,方便以后查询使用。
我们的程序在运行时消耗时间的主要有两个部分,一个部分是在CPU内部进行的计算,这里会对我们的数据进行各种逻辑处理;另一个部分是IO消耗,也就是为CPU准备数据的时间。然而CPU的处理速度和IO的处理速度相差极大,当CPU处理完一部分数据后,准备数据的时间往往比处理时间长很多,这就导致CPU会闲置下来,为了充分利用CPU的计算能力,就有了并行处理的设计,为CPU同时安排多个任务,当其中一个任务处理完了数据,在等待期间可以继续处理其他任务的数据,这样会使程序的吞吐量大幅提高。
有了并行处理的思路,接下来的问题就是设置多少个线程比较好了。由于程序在创建、销毁线程以及线程间进行切换时也会消耗CPU的计算能力,有一定的性能损耗,所以线程并不是越多越好。因此一般高并发的服务都会配置线程池,将系统中的线程进行统一的维护和管理,维护这些线程的模块叫线程池。那么线程池到底设置多大比较好呢?
线程池的大小首先与CPU的核数有关。CPU多核处理可以看作是多个CPU同时进行处理,所以至少每个核分配一个线程,这样可以保证每个核都有任务可以做。刚才提到,之所以设计多线程处理,是因为IO性能和CPU性能相差较大的缘故,所以IO耗时和CPU处理耗时的占比也会影响多线程线程数的设计。
方法一
一般有个通用计算方法,之所以加1是考虑有可能线程执行时出现错误,这时出错线程退出后可由候补线程顶上,充分利用CPU:
如果是CPU密集型应用,则线程池大小设置为N+1
如果是IO密集型应用,则线程池大小设置为2N+1
方法二
但是这样笼统的计算并不能满足所有需求,于是有了一个更细致的计算方法:
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
或者可以简化为:
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
由公式可以看出,线程等待时间越长(IO处理的时间越长),则需要越多的线程,反正则线程数越少。这与方法一中的通用方法的趋势也相符。
代码
这里将如何合理地估算线程池大小这篇文章中给出的计算线程数的代码进行整理,修改其中有误的部分,供大家参考:
首先需要定义一个线程数测试类PoolSizeCalculator:
package com.az.test;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
/**
* A class that calculates the optimal thread pool boundaries. It takes the
* desired target utilization and the desired work queue memory consumption as
* input and retuns thread count and work queue capacity.
*
* @author Niklas Schlimm
*/
public abstract class PoolSizeCalculator {
/**
* Control variable for the CPU time investigation.
*/
private volatile boolean expired;
/**
* Time (millis) of the test run in the CPU time calculation.
*/
private final long testTime = 3000;
/**
* Calculates the boundaries of a thread pool for a given {@link Runnable}.
* 计算对于给定的任务,其线程池推荐配置
*
* @param targetUtilization the desired utilization of the CPUs (0 <= targetUtilization <&#