Java Fork join:Fork所有子任务或为当前线程保留一个子任务

Java Fork join:Fork所有子任务或为当前线程保留一个子任务,java,fork-join,Java,Fork Join,我试图了解fork-join如何工作的细节 Wikipedia有下面的合并排序示例,其中左半部分是分叉的,右半部分由当前线程处理 mergesort(A, lo, hi): if lo < hi: // at least one element of input mid = ⌊(hi - lo) / 2⌋ fork mergesort(A, lo, mid) // process (potentially)

我试图了解fork-join如何工作的细节

Wikipedia有下面的合并排序示例,其中左半部分是分叉的,右半部分由当前线程处理

mergesort(A, lo, hi):
    if lo < hi:                     // at least one element of input
        mid = ⌊(hi - lo) / 2⌋
        fork mergesort(A, lo, mid)  // process (potentially) in parallel with main task
        mergesort(A, mid, hi)       // main task handles second recursion
        join
        merge(A, lo, mid, hi)
mergesort(A、lo、hi):
如果lo
但是,我所看到的大多数Java示例都是fork所有子任务并等待结果:

for (Document document : folder.getDocuments()) {
    DocumentSearchTask task = new DocumentSearchTask(document, searchedWord);
    forks.add(task);
    task.fork();
}
for (RecursiveTask<Long> task : forks) {
    count = count + task.join();
}
return count;
(文档:folder.getDocuments())的
{
DocumentSearchTask任务=新建DocumentSearchTask(文档,searchedWord);
forks.add(任务);
task.fork();
}
for(递归任务:forks){
count=count+task.join();
}
返回计数;
Wikipedia的例子对我来说更有意义,因为线程将做一些有用的事情,而不是阻塞和等待子任务

另一方面,如果我们分叉所有任务,就会避免递归,并且无法获得
stackoverflowerrror


拆分任务的首选方法是什么?为什么?

我认为首选方法是以相同的方式分叉和处理所有子任务。以下是一些原因:

  • Java中的
    ForkJoinPool
    实现了
    ExecutorService
    。请注意,
    ExecutorService
    中的所有方法都是异步的。这是有原因的-您通常可以在后台异步生成一些计算,而您的主线程可以在需要计算结果之前完成一些其他有用的工作,例如生成更多异步任务

  • 这更容易推理。如果您以相同的方式处理所有子问题,而不是在任务中引入某种不对称性,那么代码通常看起来更干净

  • 不分叉并在主线程上执行部分计算实际上没有任何优势。如果您分叉所有任务,然后等待加入,则主线程处于等待状态,几乎不消耗任何资源,工作线程可以充分利用处理器

  • 不过,这更多的是偏好问题,而不是严格的选择。除了您提到的潜在堆栈溢出之外,它们在功能上是等效的

    我不能代表这位维基百科的作者说话,但我猜她不是想让事情简单化以便解释,就是因为她有不太抽象的语言背景,在这些语言中分叉/连接不像Java那么简单



    更新:关于太多线程阻塞,这与
    ForkJoinPool
    无关。如前所述,
    ForkJoinPool
    的特殊之处在于工作窃取确实发生在
    join
    调用中

    那么你的问题是什么呢?我担心会有大量线程被阻止等待。但是Doug Lea关于fork-join的论文说,“当工作线程遇到连接操作时,它会处理其他任务(如果可用),直到发现目标任务已经完成(通过isDone)”。所以,我想把所有的子任务分开是一种更好的一致性方法。是的,我已经用一个可能感兴趣的链接更新了我的答案。我还隐式假设两个版本中的
    join
    都是ForkJoinPool连接。其他同步实际上是阻塞的,只有ForkJoinPool是特殊的。