在ThreadPoolExecutor中有一个重要的属性ctl,类型为AtomicInteger,本质是作为一个bitmap来使用。
其中包含两个域,高3位表示线程池的5中状态(Running、ShutDown、Stop、Tidying、Terminated),低29位表示线程池的数量。
因此,理论上,线程池的最大容量为2^29-1=536870911(5亿多)。
但是实际生产中我们通常需要指定一个合理的线程池容量,5亿没有什么现实意义。那么如何合理的设置我们的线程池容量呢?
在《Java并发编程实践》中提到,对于计算密集型的任务,一个具有N个CPU的处理器的系统,通常通过使用N+1个线程的线程池来获得最优的使用率(计算密集型的线程恰好在某时因为发生一个页错误或因为其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作)。对于包含了I/O和其他阻塞操作的任务,不是所有的线程都会在所有的时间被调度,因此需要一个更大的池。为了正确的设置线程池的容量,必须估算任务花在等待的时间与用来计算的时间的比率;这个估算值不必十分精确,而且可以通过一些监控工具获得。还可以选择另一种方法来调节线程池的大小,在一个基准负载下,使用几种不同大小的线程池运行我们的应用,并观察CPU利用率水平。
约束线程池容量的因素还有很多:内存、文件句柄、套接字句柄、数据库连接等。计算这类资源池的大小约束很简单:首先累加每一个任务需要的这些资源的总量,然后除以可用的总量。所得的结果是池容量的上限。
基于以上的理论基础,我们的线程池的核心容量(最小容量)应该设置为可用的CPU核数。对于计算密集型的任务,这个容量就足够