Java ForkJoinPool在调用ALL/join期间暂停

Java ForkJoinPool在调用ALL/join期间暂停,java,java.util.concurrent,lock-free,fork-join,Java,Java.util.concurrent,Lock Free,Fork Join,我尝试使用一个并行化的CPU密集型计算。 我对ForkJoinPool的理解是,只要可以执行任何任务,它就会继续工作。不幸的是,我经常观察到工作线程处于空闲/等待状态,因此并非所有CPU都处于繁忙状态。有时我甚至观察到额外的工作线程 我没有预料到这一点,因为我严格地尝试使用任务。 我的观察结果与他的非常相似。 在对ForkJoinPool进行了大量调试之后,我有一个猜测: 我使用invokeAll()将工作分配到一系列子任务上。invokeAll()完成执行第一个任务后,它开始加入其他任务。这可

我尝试使用一个并行化的CPU密集型计算。 我对ForkJoinPool的理解是,只要可以执行任何任务,它就会继续工作。不幸的是,我经常观察到工作线程处于空闲/等待状态,因此并非所有CPU都处于繁忙状态。有时我甚至观察到额外的工作线程

我没有预料到这一点,因为我严格地尝试使用任务。 我的观察结果与他的非常相似。 在对ForkJoinPool进行了大量调试之后,我有一个猜测:

我使用invokeAll()将工作分配到一系列子任务上。invokeAll()完成执行第一个任务后,它开始加入其他任务。这可以正常工作,直到下一个要加入的任务位于执行队列的顶部。不幸的是,我异步提交了其他任务,但没有加入它们。我希望ForkJoin框架首先继续执行这些任务,然后再返回到加入任何剩余的任务

但似乎不是这样。相反,工作线程会暂停调用wait(),直到等待的任务准备就绪(可能由另一个工作线程执行)。我没有验证这一点,但这似乎是调用join()的一个普遍缺陷

ForkJoinPool提供了,但这是一个全局参数,不能用于单个提交。但我喜欢看到我的异步分叉任务很快就会执行


那么,为什么ForkJoinTask.doJoin()不简单地在其队列顶部执行任何可用的任务,直到它准备就绪为止(要么由自身执行,要么被他人窃取)

关于join(),你完全正确。两年前我写了一篇文章,指出了join()的问题

正如我在这里所说的,在完成之前的请求之前,框架无法执行新提交的请求。并且每个工作线程在其当前请求完成之前无法窃取,这将导致等待()


您看到的其他线程是“延续线程”。由于join()最终会发出wait(),因此需要这些线程,以便整个框架不会暂停。

由于似乎没有其他人理解我的问题,我尝试解释几晚调试后发现的问题:

如果所有fork/join调用都严格成对,那么ForkJoinTasks的当前实现工作得很好。用一个开括号表示叉,用一个闭括号表示叉,完美的二进制叉连接模式可能如下所示:

{([])([])}{([])([])}

如果使用invokeAll(),还可以提交如下子任务列表:

{([])([])([])([])}

然而,我所做的看起来像这样:

{([)([)}…]

您可能会认为这看起来很糟糕,或者是对fork-join框架的滥用。但唯一的限制是,否则你可能会陷入僵局。只要我的[]任务不依赖于()任务,我就看不出有任何问题。冒犯的]只是表示我没有明确地等待他们;他们可能有一天会结束,这对我(那时)来说并不重要

实际上,当前的实现能够执行我的互锁任务,但只能通过生成额外的助手线程来执行,这是非常低效的

该漏洞似乎是join()的当前实现:加入一个期望看到它相应的在其执行队列的顶部,但它发现了一个[,并且感到困惑。当前线程挂起(调用wait())而不是简单地执行[来摆脱它直到其他人过来执行意外任务。这会导致性能急剧下降


我的主要目的是在队列上添加额外的工作,以防止工作线程在队列为空时挂起。不幸的是,情况正好相反:-(

您没有将此框架用于其预期的非常狭窄的用途

该框架从2000年的研究论文中的实验开始。从那时起,它就被修改了,但基本设计,即大型阵列上的分叉和联接,仍然保持不变。其基本目的是教本科生如何沿着平衡树的叶子走。当人们用它来处理简单阵列以外的东西时,会发生奇怪的事情.我无法理解它在Java7中所做的事情;这就是本文的目的

Java8中的问题只会变得更糟。它是驱动所有流并行工作的引擎。请阅读该文章的第二部分。lambda兴趣列表中充满了线程暂停、堆栈溢出和内存不足错误的报告


如果不将其用于大型数据结构的纯递归分解,则使用它的风险自负。即使如此,它所创建的过多线程也可能造成严重破坏。我不打算进一步讨论这个问题。

你能与我们分享一些代码吗?有时3行代码就意味着30多行文字。我在这里放了一句话:是的,我读了你的文章,这确实让我得出了我的结论。我同意你的观点,F/J框架无法处理被外部资源阻止的任务。但我仍然不明白为什么WorkerThread不能执行任何其他可用的工作,无论是从任何尾部偷来的还是从它自己的顶部拿走的,直到等待的任务完成ready.Accountability。如果另一个请求的任务中存在异常,则可能无法找到所有者。池仅使用一个未捕获的ExceptionHandler。您可以自己查看代码。这是一个混乱的代码,但您可以跟踪它。不确信:如果执行从另一个队列窃取的任务,则会出现相同的问题。T当前FJTask负责捕获这些任务并调用此.setExceptionalCompletion(),而不是将它们传递给池UncaughtExceptionHandler。问题仍然是:为什么不从