我正在开发一个标准的服务器应用程序。每个请求的处理被分解成几个阶段(第二阶段要求第一个阶段完成,等等)。现在,这些阶段中的一个需要相当长的时间,但它本身可以分解成几十个任务,这些任务彼此不依赖,因此是可并行的。我想向 SlowStageService
添加一个线程池,并且想知道如何最好地调整它的大小。大多数时候线程池最好至少有一个空缺,这意味着它必须以与服务器接收请求一样快或更快的速度处理任务;这产生了线程池大小的合理下限。但是,我想在尺寸上大方一些,因为计算中涉及的许多数字可能会发生变化。
所以我的问题是:使我的池太大(比如 3 或 4 倍)并且有很多空闲线程的缺点是什么?我知道它浪费了一些资源,但它实际上并没有保留任何 CPU 并因此使其无法用于其他请求,对吗?通常可以在实践中有多少懈怠?假设我计算出 6 个线程是我需要的最小值;在中等负载下在 24 核盒子上说 12 是否安全?
在我写这篇文章时,听起来我可能需要的是一个范围相当窄(3 或 4 倍)的 ThreadPoolExecutor
?
最佳答案
您应该引用 Brian Goetz 所著的《Java 并发实践》一书中的第 8.2 节。
如果您的阶段是计算密集型阶段并且 I/O 非常少,那么线程池的最佳大小 = CPU 核心数 + 1(因此在您的情况下为 25)。 但是,如果您的阶段中存在 I/O 绑定(bind)任务,那么实现最佳响应时间的线程池的最佳适用大小取决于多种因素:
- CPU 数量 - N
- CPU 的目标利用率 - UC
- I/O 操作的等待时间(阻塞状态)(W) 与计算时间 (C) 的比率。
NUM_THREADS = N * UC * (1+(W/C))
除了上述指标外,影响池大小计算的其他因素还有内存要求、连接池大小、文件句柄和套接字句柄。
鉴于上述调整线程池大小的理论;我的经验表明,获得最佳池大小的最佳方法是分析各种工作负载下的应用程序,并针对轻型、中型和重型工作负载大小得出池大小。
此外,永远不要对最大线程池大小进行硬编码 - 它应该始终是一个可配置参数,以便可以根据遇到的工作负载在现场进行调整。
关于java - 创建大型固定线程池的后果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34824142/