当前位置: 首页 > 面试题库 >

Java ForkJoinPool具有非递归任务,窃取工作有效吗?

淳于泓
2023-03-14
问题内容

我想Runnable通过一种方法将任务提交到ForkJoinPool中:

forkJoinPool.submit(Runnable task)

注意,我使用的是JDK 7。

在后台,它们被转换为ForkJoinTask对象。我知道,当将任务递归拆分为较小的任务时,ForkJoinPool是有效的。

题:

如果没有递归,偷窃工作是否仍可以在ForkJoinPool中进行?

在这种情况下值得吗?

更新1: 任务很小,可以不平衡。即使对于严格相等的任务,诸如上下文切换,线程调度,停放,​​页面丢失等问题也会导致 不平衡

更新2: Doug Lea在并发JSR-166兴趣小组中写了一个提示:

当所有任务都异步并提交到池中而不是分叉时,这也大大提高了吞吐量,这成为构造actor框架以及许多其他可能使用ThreadPoolExecutor的普通服务的合理方法。

我认为,当涉及到较小的CPU限制任务时,由于进行了这种优化,ForkJoinPool是必经之路。要点是这些任务已经很小,不需要递归分解。无论是大任务还是小任务,
窃取 工作都是有效的-任务可以由另一名自由工作者从忙碌的工人的Deque尾巴抓住。

更新3: ForkJoinPool的可伸缩性-
Akka
乒乓球团队进行的基准测试显示了很好的结果。

尽管如此,要更有效地应用ForkJoinPool需要进行性能调整。


问题答案:

ForkJoinPool源代码有一个不错的部分,称为“实施概述”,请阅读以获取最终真相。以下说明是我对JDK 8u40的理解。

从第一天开始,ForkJoinPool每个工作线程都有一个工作队列(我们称它们为“工作队列”)。分叉的任务被推入本地工作线程队列,准备再次由工作线程弹出并执行-
换句话说,从工作线程角度看,它看起来像是一个堆栈。当工作人员耗尽其工作人员队列时,它将四处走动,并尝试从其他工作人员队列中窃取任务。那就是 “偷工作”

现在,在(IIRC)JDK 7u12之前,ForkJoinPool只有一个全局 提交队列
。当工作线程用尽本地任务以及要偷的任务时,他们到达那里并尝试查看是否有外部工作可用。在这个设计中,有对有规律的,比如,没有优势ThreadPoolExecutor的支持ArrayBlockingQueue

此后发生了很大变化。在确定此提交队列是严重的性能瓶颈之后,Doug
Lea等人。划分提交队列。事后看来,这是一个显而易见的主意:您可以重用大多数可用于工作队列的机制。您甚至可以为每个工作线程松散地分配这些提交队列。现在,外部提交进入提交队列之一。然后,没有工作需要上班的工作人员可以先查看与特定工作人员相关联的提交队列,然后四处逛逛以查看其他人的提交队列。人们也可以称其
“偷工作”。

我已经看到许多工作负载从中受益。ForkJoinPool甚至对于普通的非递归任务而言,这种特殊的设计优势早已为人所认识。许多在concurrency-
interest
@上的用户要求一个简单的,可以偷工减料的执行ForkJoinPool器。这就是为什么我们Executors.newWorkStealingPool()要从JDK
8开始的原因之一-当前委托给ForkJoinPool,但是愿意提供更简单的实现。



 类似资料:
  • 我试图理解工作窃取对递归任务的影响:工作窃取的一个优点是,当前的工作线程/线程可能会执行自己的生成任务;增加数据局部性。但是,在常见情况下,当工作线程加入其生成的任务时会发生什么?例如: 我认为这里当前线程会被阻塞,因此无法从自己的队列中获取工作,因此另一个工作人员将不得不窃取这些工作。这将否认工作窃取的局部优势。然而,根据维基百科(https://en.wikipedia.org/wiki/Wo

  • 在我的项目中,我正在构建一个Java的执行框架,它接收来自客户端的工作请求。工作(大小不同)被分解为一组任务,然后排队等待处理。有单独的队列来处理每种类型的任务,每个队列都与一个ThreadPool相关联。ThreadPools的配置方式使引擎的整体性能达到最佳。 这种设计有助于我们有效地平衡请求的负载,大型请求不会占用系统资源。然而,当一些队列为空并且它们各自的线程池闲置时,该解决方案有时会变得

  • 问题内容: 我期望以下内容返回所有元组,将层次结构中的每个父级解析到顶部,但它仅返回最低级别(在查询中指定了其ID)。如何为给定的level_id返回整棵树? 问题答案: 首先,如果您确实是祖父母,则应该如此。其次,您在查询的递归一半中的(隐式)连接条件是向后的,您想让父级脱离,而不是:

  • 我想通过以下方法将<code>Runnable<code>任务提交到ForkJoinPool: 注意,我使用JDK 7。 在引擎盖下,它们被转换为ForkJoinTask对象。我知道ForkJoinPool在任务递归地分成较小的任务时是有效的。 问题: 如果没有递归,工作窃取在ForkJoinPool中仍然有效吗? 在这种情况下值得吗? 更新1:任务小,可以不平衡。即使对于严格相等的任务,如上下文

  • 从java文档, ForkJoinPool不同于其他类型的ExecutorService,主要是因为它采用了工作窃取:池中的所有线程都试图查找并执行其他活动任务创建的子任务(如果不存在,则最终阻塞等待工作)。 当大多数任务产生其他子任务时(就像大多数ForkJoinTasks一样),这可以实现高效处理。当在构造函数中将asyncMode设置为true时,ForkJoinPools也可能适合用于从未

  • 例如,工作窃取在Java平台上的Fork/Join框架中可用。(请参阅fork/Join框架如何比线程池更好?)-OmniThreadLibrary是否可能有类似的东西? 工作窃取:工作线程用完了要做的事情,可以从其他仍然繁忙的线程中窃取任务。