我们说我们扩展了一个名为RecursiveTask
的{{1}}。
然后在forkJoinTask的范围内创建两个子任务:
MyRecursiveTask
我认为" t2"将位于Workqueue的顶部(这是一个deque,它被用作worker本身的堆栈),因为我看到了fork方法的实现,如下所示:
MyRecursiveTask t1 = new MyRecursiveTask()
MyRecursiveTask t2 = new MyRecursiveTask()
t1.fork()
t2.fork()
如果是这样,下面两个表达式的性能是否存在差异:
表达式1:
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
((ForkJoinWorkerThread)t).workQueue.push(this);
else
ForkJoinPool.common.externalPush(this);
return this;
}
表达式2:
t1.join() + t2.join()
我认为这可能很重要。在t2.join() + t1.join()
完成之前,t1.join()
将始终阻塞(如果没有工作窃取),因为只有workQueue顶部的任务才能被激活。 (换句话说,t2必须在t1加速之前加速)。以下是t2.join()
和doJoin
的代码。
tryUnpush
有没有人有这方面的想法?谢谢!
答案 0 :(得分:5)
您使用Java7还是Java8很重要。在Java7中,框架为join()创建了延续线程。在Java8中,框架主要用于join()。 See here. 自2010年以来,我一直在撰写关于此框架的评论。
使用RecursiveTask(来自JavaDoc)的建议:
return f2.compute() + f1.join();
这样分裂线程就会继续操作。
由于此代码经常更改,因此不建议依赖F / J代码进行指导。例如,在Java8中使用嵌套并行流导致过多的补偿线程,代码在Java8u40中重新编写只会导致更多问题。 See here
如果你必须进行多次连接,那么加入()的顺序并不重要。每个fork()都可以为任何线程提供任务。
答案 1 :(得分:0)
如果你有足够的核心数量而不是两个并行运行的线程,那么首先启动哪一个并不重要,因为完成时间很重要。因此,无论哪一个完成,第一个必须等待另一个完成并计算结果。如果你有1个核心而不是你认为可能是真的,但对于1个核心你为什么需要并行化这个工作呢?