我希望通过一种方法将Runnable任务提交到ForkJoinPool中:
forkJoinPool.submit(Runnable task)注意,我使用JDK 7。
在遮罩下,它们被转换为ForkJoinTask对象。我知道,当任务被递归地拆分为较小的任务时,ForkJoinPool是有效的。
问题:
如果没有递归,偷窃工作在ForkJoinPool中仍然有效吗?
在这种情况下值得吗?
更新1:任务很小,可能是不平衡的。即使对于完全相同的任务,诸如上下文切换、线程调度、停车、页面丢失等也会导致不平衡的。
更新2: Doug在并发JSR-166利息组中给出了提示:
当所有任务都是异步的并提交到池而不是分叉时,这也大大提高了吞吐量,这成为构造参与者框架的合理方法,以及许多可能使用ThreadPoolExecutor的普通服务。
我猜想,当涉及到相当小的CPU绑定任务时,ForkJoinPool是可行的,这要归功于这种优化。主要的一点是,这些任务已经很小,不需要递归分解。Work-stealing可以工作,不管它是大任务还是小任务--任务可以被另一个自由工作者从忙碌工人的尾巴上抓取。
更新3: ForkJoinPool的可伸缩性 -由阿克卡的乒乓球团队标杆显示了伟大的结果。
尽管如此,为了更有效地应用ForkJoinPool,需要进行性能调优。
发布于 2015-05-06 18:54:21
ForkJoinPool源代码有一个很好的部分,叫做“实现概述”,为最终的真理而阅读。下面的解释是我对JDK 8u40的理解。
从第一天起,ForkJoinPool就为每个工作线程设置了一个工作队列(让我们称它们为“工作队列”)。分叉式任务被推入本地工作队列,准备好再次由工作人员弹出并被执行--换句话说,从工作者线程的角度看,它就像一个堆栈。当一个工作人员耗尽它的工作队列时,它会四处移动,并试图从其他工作队列中窃取任务。那就是“偷工作”。
现在,在(IIRC) JDK7u12之前,ForkJoinPool只有一个全局提交队列。当工作线程耗尽本地任务以及要窃取的任务时,它们会到达那里,并试图查看外部工作是否可用。在这种设计中,与常规的,比如说,由ThreadPoolExecutor支持的ArrayBlockingQueue相比,没有优势。
此后,情况发生了很大变化。在此提交队列被确定为严重的性能瓶颈之后,Doug等人。对提交队列进行条带化。事后看来,这是一个显而易见的想法:您可以重用大多数工人队列可用的机制。您甚至可以将这些提交队列松散地分发给每个工作线程。现在,外部提交进入一个提交队列。然后,没有工作可处理的工作人员可以首先查看与特定工作人员关联的提交队列,然后四处查看其他人的提交队列。人们也可以称之为“偷工作”。
我已经看到许多工作负载从中受益。即使对于普通的非递归任务,ForkJoinPool的这一特殊的设计优势也很久以前就已经被认识到了。许多在并发利息@的用户要求一个简单的工作-窃取执行者没有所有的ForkJoinPool技能。这就是为什么我们在JDK 8中继续使用Executors.newWorkStealingPool()的原因之一--目前委托给ForkJoinPool,但开放以提供更简单的实现。
https://stackoverflow.com/questions/30047122
复制相似问题