我刚阅读这篇文章:What's the advantage of a Java-5 ThreadPoolExecutor over a Java-7 ForkJoinPool?,觉得答案不够直接。
你能用简单的语言和例子解释一下,Java 7的Fork-Join框架和旧的解决方案之间的权衡是什么?
我也读了谷歌的#1击中主题Java Tip: When to use ForkJoinPool vs ExecutorService从javaworld.com,但文章没有回答标题问题当,它谈论的API差异主要是...
6条答案
按热度按时间nbysray51#
Fork-join允许您轻松执行分治作业,如果您想在
ExecutorService
中执行它,则必须手动实现。在实践中,ExecutorService
通常用于并发处理许多独立请求(又名事务),当您想要加速一个连贯的作业时,可以使用fork-join。gv8xihay2#
Fork-join特别适合于 * 递归 * 问题,其中一个任务涉及运行子任务,然后处理它们的结果。(这通常被称为“分而治之”......但这并没有揭示本质特征。)
如果你试图使用传统的线程(例如通过ExecutorService)来解决这样的递归问题,你最终会发现线程被捆绑起来,等待其他线程向它们传递结果。
另一方面,如果问题不具备这些特征,那么使用fork-join就没有真实的的好处。
参考文献:
u3r8eeie3#
Java 8在Executors中提供了另一个API
使用所有可用处理器作为其目标并行度级别创建工作窃取线程池。
通过添加此API,Executors提供了不同类型的ExecutorService选项。
根据您的需求,您可以选择其中之一,或者您可以寻找ThreadPoolExecutor,它可以更好地控制有限任务队列大小,
RejectedExecutionHandler
机制。1.一米一米一
创建一个线程池,该线程池重用在共享无界队列上运行的固定数量的线程。
1.一米二米一x
创建一个线程池,该线程池可以计划命令在给定延迟后运行,或定期执行。
1.一米三米一x
创建一个线程池,该线程池根据需要创建新线程,但在以前构造的线程可用时将重用这些线程,并在需要时使用提供的ThreadFactory创建新线程。
1.***
static ExecutorService newWorkStealingPool(int parallelism)
***创建一个线程池,该线程池维护足够的线程以支持给定的并行级别,并且可以使用多个队列来减少争用。
这些API中的每一个都旨在满足应用程序的相应业务需求。使用哪一个取决于您的用例需求。
例如
1.如果您希望按到达顺序处理所有提交的任务,只需使用
newFixedThreadPool(1)
1.如果要优化递归任务的大计算性能,请使用
ForkJoinPool
或newWorkStealingPool
1.如果希望定期或在将来某个时间执行某些任务,请使用
newScheduledThreadPool
相关SE问题:
java Fork/Join pool, ExecutorService and CountDownLatch
ru9i0ody4#
Brian Goetz对这种情况的描述最好:https://www.ibm.com/developerworks/library/j-jtp11137/index.html
使用传统的线程池来实现fork-join也具有挑战性,因为fork-join任务花费了它们生命周期的大部分时间来等待其他任务。这种行为是线程饥饿死锁的一个处方,除非仔细选择参数来限制所创建的任务的数量,或者线程池本身是无限的。传统的线程池是为彼此独立的任务设计的,并且也被设计为具有潜在的阻塞,粗粒度的任务- fork-join解决方案两者都不产生。
我推荐阅读整篇文章,因为它有一个很好的例子来说明为什么要使用fork-join池。它是在ForkJoinPool成为官方之前写的,所以他提到的
coInvoke()
方法变成了invokeAll()
。soat7uwm5#
Fork-Join框架是Executor框架的一个扩展,专门解决递归多线程程序中的“等待”问题。事实上,新的Fork-Join框架类都是从Executor框架的现有类扩展而来的。
Fork-Join框架有两个核心特征
如果并行处理需求是严格递归的,则别无选择,只能使用Fork-Join,否则执行器或Fork-Join框架都应该这样做,尽管Fork-Join可以说更好地利用了资源,因为空闲线程从繁忙线程“窃取”了一些任务。
sczxawaw6#
Fork Join是ExecuterService的一个实现。主要的区别是这个实现创建了DEQUE工作线程池。其中任务从一侧插入,但从任何一侧撤回。这意味着如果你已经创建了
new ForkJoinPool()
,它将寻找可用的CPU并创建那么多的工作线程。然后它将负载均匀地分布在每个线程上。但是如果一个线程工作得很慢,而其他线程工作得很快,他们会从慢线程中选择任务。从后面。下面的步骤将更好地说明窃取。阶段1(初始):
W1 -〉5,4,3,2,1
W2 -〉10,9,8,7,6
第二阶段:
W1 -〉5,4
W2 -〉10,9,8,7,
第三阶段:
W1 -〉10,5,4
W2 -〉9、8、7,
而Executor服务创建请求的线程数,并应用一个阻塞队列来存储所有剩余的等待任务。如果您使用了cachedExecuterService,它将为每个作业创建单个线程,并且不会有等待队列。