前言
大家都用过线程池,但是线程池数量设置为多少比较合理呢?
线程数的设置的最主要的目的是为了充分并合理地使用 CPU 和内存等资源,从而最大限度地提高程序的性能,因此让我们一起去探索吧!
首先要考虑到 CPU 核心数,那么在 Java 中如何获取核心线程数?
可以在代码中调用 Runtime.getRuntime().availableProcessor() 方法来获取(可能不准确,作为参考)或者直接上 linux 服务器命令查看 lscpu
方案
❝
首先确认了核心数后,我们需要分析下线程池处理的程序是 「CPU密集型(CPU-bound)」、「IO密集型(I/O bound)」,针每种类型的程序我们有不同的配置方案。
❞
CPU密集型(CPU-bound)
CPU密集型也就是**「计算密集型,该任务需要大量的运算,而没有阻塞,CPU 一直全速运行」**。
CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核 CPU 上,无论你开几个模拟的多线程,该任务都不可能得到加速,因为CPU总的运算能力就那些。 在《Java虚拟机并发编程》中,推荐将CPU密集型最大线程数设置为 最大线程数 = CPU核心数 + 1,这样能发挥最高效率。
核心线程数一般会设置为 核心线程数 = 最大线程数 * 20% 如下图所示,CPU密集型更多的是计算任务会持续占用CPU。
IO密集型(I/O bound)
IO密集型是指我们程序更多的工作是 「通过 MySQL 数据库、文件的读写、网络通信等读取数据」,在IO期间我们线程是阻塞的,在单线程上运行IO密集型任务会导致浪费大量的CPU运算能力浪费在等待。「所以IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要利用了被浪费掉的阻塞时间」。
通常在进行接口远程调用,数据库数据获取,缓冲数据获取都属于IO操作。 这时我们线程数可以通过以下公式进行计算:最大线程数 = CPU核心数 / (1 - 阻塞占百分比)。
我们很好理解比如在某个请求中,请求时长为10秒,调用IO时间为8秒,这时我们阻塞占百分比就是80%,有效利用CPU占比就是20%,假设是八核CPU,我们线程数就是8 / (1 - 80%) = 8 / 0.2 = 40个。
也就是说 我们八核CPU在上述情况中,可满负荷运行40个线程。这时我们可将最大线程数调整为40,在系统进行IO操作时会去处理其他线程。 通常我们会设置为 最大线程数 = CPU核心数 * 2
核心线程数一般会设置为 核心线程数 = 最大线程数 * 20%
如下图所示,IO密集型是同一个任务只有部分时间是在进行计算,很长时间都在进行IO阻塞。这样CPU同时刻就能处理更多的任务。
总结
即使有上面的简单估算方法,也许看似合理,但实际上也未必合理,都需要结合系统真实情况(比如是IO密集型或者是CPU密集型或者是纯内存操作)和硬件环境(CPU、内存、硬盘读写速度、网络状况等)来不断尝试达到一个符合实际的合理估算值。
我的微信公众号:Java架构师进阶编程
专注分享Java技术干货,期待你的关注!