我在比较测试程序中的两个变体。这两种设备都是在一台有四个核心的机器上使用4线程ForkJoinPool操作的.
在‘模式1’中,我使用池非常像一个执行者服务。我把一堆任务扔到ExecutorService.invokeAll中。与普通的固定线程执行器服务相比,我获得了更好的性能(尽管有对Lucene的调用,在其中执行一些I/O操作)。
这里没有分而治之的地方。从字面上讲,我知道
ExecutorService es = new ForkJoinPool(4);
es.invokeAll(collection_of_Callables);在“模式2”中,我向池提交一个任务,并在该任务中调用ForkJoinTask.invokeAll提交子任务。因此,我有一个从RecursiveAction继承的对象,它被提交到池中。在该类的计算方法中,我对来自不同类的对象集合调用invokeAll,这些对象也继承自RecursiveAction。为了测试目的,我只在第一次提交一次对象.我天真地期望看到所有四个线程都在忙什么,因为调用invokeAll的线程将为自己抓取一个子任务,而不只是坐以待毙。我可以想出一些原因,为什么它可能不能这样工作。
在VisualVM中,在模式2中,有一个线程几乎总是在等待。我希望看到的是,调用invokeAll的线程将立即处理其中一个被调用的任务,而不是静坐不动。这当然比用普通线程池尝试这个方案所造成的死锁要好,但是,怎么回事?它是否保持一个线程后退,以防其他东西被提交?如果是的话,为什么模式1中不存在同样的问题呢?
到目前为止,我一直在使用添加到java1.6的引导类路径的jsr166 jar来运行它。
发布于 2012-03-14 18:24:04
ForkJoinTask.invokeAll是所有任务的分叉,但在列表中第一位。它自己运行的第一个任务。然后加入其他任务。它的线程不会以任何方式释放到池中。所以你所看到的,是线程阻塞了其他任务才能完成。
发布于 2012-03-15 03:03:14
对于Fork池,invokeAll的典型用法是分叉一个任务,然后计算另一个任务(在执行线程中)。没有分叉的线程将在计算后加入。偷盗工作伴随着这两项任务的计算。当每个任务计算时,预期它会分叉它自己的子任务(直到达到某个阈值)。
我不知道为您的invokeAll调用了什么RecursiveAction.compute(),但是如果invokeAll需要两个RecursiveAction,它将分叉一个,计算另一个,等待分叉的任务完成。
这与普通的执行程序服务不同,因为ExecutorService的每个任务都只是队列上的一个可运行的任务。不需要ExecutorService的两个任务就可以知道另一个任务的结果。这是FJ的主要用例。
https://stackoverflow.com/questions/9677506
复制相似问题