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