何时创建新的 ForkJoinPool 以及何时使用 CommonPool?

jok*_*ker 6 java multithreading forkjoinpool

我正在阅读线程并了解 fork/join API。

我发现您可以使用 commonPool 作为管理线程的默认池运行线程,或者我可以将线程提交到新创建的 ForkJoinPool。

两者之间的区别如下,据我所知:

  • commonPool 是静态创建的主池(其中一些池方法不像它们通常对其他池那样工作,例如关闭它),并且主要用于运行应用程序。
  • default/commonPool 中的并行数是内核数 - 1,其中新创建的池的默认并行数 = 内核数(或系统属性指定的数量parallelism- 我忽略了完全限定的系统属性键名-)。

根据文档,commonPool 适用于大多数用途。

这一切都归结为我的问题:

什么时候应该使用公共池?为什么会这样?我应该什么时候创建一个新池?为什么会这样?

Nik*_*hev 5

短篇故事

答案,就像在软件工程中的大多数事情,就是:“这取决于”。

使用公共池的优点

如果你看这篇精彩的文章:

根据 Oracle 的文档,使用预定义的公共池会减少资源消耗,因为这会阻止为每个任务创建单独的线程池。

使用 fork/join 框架可以加快大型任务的处理速度,但要实现此结果,应遵循一些准则:

  • 使用尽可能少的线程池——在大多数情况下,最好的决定是为每个应用程序或系统使用一个线程池
  • 使用默认的公共线程池,如果不需要特殊调优
  • 使用合理的阈值将 ForkJoingTask 拆分为子任务
  • 避免 ForkJoingTasks 中的任何阻塞

使用专用池的优点

然而,也有一些争论反对采取这一方式:

复杂应用的专用池

在复杂的应用程序中,每个逻辑工作单元都有一个专用池有时是首选方法。想象一个应用程序:

  1. 接收大量事件并将它们分组(可以并行完成
  2. 然后工作人员完成工作(也可以并行完成
  3. 最后,一些清理工作者会进行一些清理(也可以并行完成)。

因此,您的应用程序有 3 个逻辑工作组,每个工作组可能都有自己的并行需求。(请记住,这个池的并行度在大多数机器设置得相当低

最好不要踩到对方的脚趾,对吧?请注意,这可以扩展到某个级别,建议为每个工作单元提供单独的微服务,但是如果由于某种原因您还没有在那里,那么每个逻辑工作单元的专用 forkJoinPool不是一个坏主意


其他图书馆

如果您的应用程序的代码只有一个地方需要并行性,则您无法保证某些开发人员不会拉一些同样依赖于公共 ForkJoinPool 的 3-rd 方依赖项,并且您仍然有两个地方可以使用此池有需求。这对于您的用例来说可能没问题,也可能不是,特别是如果您的默认池的并行度为 4 或更低。

想象一下当您的应用程序关键代码(例如事件处理或将数据保存到数据库)必须与某个竞争公共池时的情况,该库将日志并行导出到某个日志接收器。


专用的 ForkJoinPool 使日志记录更整洁

此外,常见的 forkJoinPool 具有相当非描述性的命名,因此如果您正在调试或查看日志,您可能需要筛选大量

ForkJoinPool.commonPool-worker-xx

在上述情况下,将其与:

ForkJoinPool.grouping-worker-xx

ForkJoinPool.payload-handler-worker-xx

ForkJoinPool.cleanup-worker

因此,您可以看到当每个逻辑工作组使用专用的 ForkJoinPool 时,日志记录清洁一些好处


TL; 博士

使用通用的 ForkJoinPool具有更低的内存影响更少的资源和线程创建以及更低的垃圾收集需求。但是,如上所述,对于某些用例,这种方法可能是不够的。

在您的应用程序中每个逻辑工作单元使用专用的 ForkJoinPool可提供更整洁的日志记录当您的并行度级别较低(即内核不多)时,以及当您想避免应用程序逻辑上不同部分之间的线程争,使用这不是一个坏主意. 然而,这是以更高的 CPU 利用率、更高的内存开销和更多线程创建为代价的。