与Java-7 ForkJoinPool相比,Java-5 ThreadPoolExecutor有什么优势? [英] What's the advantage of a Java-5 ThreadPoolExecutor over a Java-7 ForkJoinPool?

查看:479
本文介绍了与Java-7 ForkJoinPool相比,Java-5 ThreadPoolExecutor有什么优势?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java 5引入了对以Executor框架形式的线程池执行异步任务的支持,其核心是由java.util.concurrent.ThreadPoolExecutor实现的线程池. Java 7以java.util.concurrent.ForkJoinPool的形式添加了备用线程池.

看看它们各自的API,ForkJoinPool在标准方案中提供了ThreadPoolExecutor功能的超集(尽管严格来说,ThreadPoolExecutor比ForkJoinPool提供了更多的调优机会).除此之外,观察到 fork/join任务似乎更快(可能是由于偷窃了调度程序),肯定需要更少的线程(由于非阻塞的join操作),可能给人的印象是ThreadPoolExecutor已被ForkJoinPool取代.

但这真的正确吗?我已阅读的所有材料似乎总结出两种类型的线程池之间的模糊对比:

  • ForkJoinPool适用于许多依赖于任务的,生成的,短暂的,几乎没有阻塞(即计算密集型)的任务
  • ThreadPoolExecutor用于很少的,独立的,外部生成的,较长的,有时是阻塞的任务

这种区别是正确的吗?我们能说些什么更具体吗?

解决方案

ThreadPool(TP)和ForkJoinPool(FJ)针对不同的用例.主要区别在于不同执行者使用的队列数量决定了哪种类型的问题更适合任一执行者.

FJ执行器具有n个(又是并行性级别)单独的并发队列(双端队列),而TP执行器只有一个并发队列(这些队列/双端队列可能是不遵循JDK Collections API的自定义实现).结果,在您生成大量(通常运行时间相对较短)任务的情况下,FJ执行程序的性能会更好,因为独立队列将最大程度地减少并发操作,而很少的窃取将有助于负载平衡.在TP中,由于只有一个队列,所以每次将工作出队时都会有并发操作,这将成为一个相对的瓶颈并限制性能.

相反,如果长期运行的任务相对较少,则TP中的单个队列不再是性能的瓶颈.但是,n无关的队列和相对频繁的偷窃尝试现在将成为FJ的瓶颈,因为可能会有很多徒劳的偷窃尝试,这会增加开销.

此外,FJ中的工作窃取算法假设从双端队列中窃取的(较旧的)任务将产生足够的并行任务以减少窃取次数.例如.在较早的任务等同于较大阵列的quicksort或mergesort中,这些任务将生成更多任务,并使队列为非空,并减少总体窃取次数.如果在给定的应用程序中不是这种情况,那么频繁的窃取尝试将再次成为瓶颈.在 ForkJoinPool 的javadoc中也对此进行了说明:

此类提供状态检查方法(例如getStealCount()) 旨在帮助开发,调整和监视 分叉/联接应用程序.

Java 5 has introduced support for asynchronous task execution by a thread pool in the form of the Executor framework, whose heart is the thread pool implemented by java.util.concurrent.ThreadPoolExecutor. Java 7 has added an alternative thread pool in the form of java.util.concurrent.ForkJoinPool.

Looking at their respective API, ForkJoinPool provides a superset of ThreadPoolExecutor's functionality in standard scenarios (though strictly speaking ThreadPoolExecutor offers more opportunities for tuning than ForkJoinPool). Adding to this the observation that fork/join tasks seem to be faster (possibly due to the work stealing scheduler), need definitely fewer threads (due to the non-blocking join operation), one might get the impression that ThreadPoolExecutor has been superseded by ForkJoinPool.

But is this really correct? All the material I have read seems to sum up to a rather vague distinction between the two types of thread pools:

  • ForkJoinPool is for many, dependent, task-generated, short, hardly ever blocking (i.e. compute-intensive) tasks
  • ThreadPoolExecutor is for few, independent, externally-generated, long, sometimes blocking tasks

Is this distinction correct at all? Can we say anything more specific about this?

解决方案

ThreadPool (TP) and ForkJoinPool (FJ) are targeted towards different use cases. The main difference is in the number of queues employed by the different executors which decide what type of problems are better suited to either executor.

The FJ executor has n (aka parallelism level) separate concurrent queues (deques) while the TP executor has only one concurrent queue (these queues/deques maybe custom implementations not following the JDK Collections API). As a result, in scenarios where you have a large number of (usually relatively short running) tasks generated, the FJ executor will perform better as the independent queues will minimize concurrent operations and infrequent steals will help with load balancing. In TP due to the single queue, there will be concurrent operations every time work is dequeued and it will act as a relative bottleneck and limit performance.

In contrast, if there are relatively fewer long-running tasks the single queue in TP is no longer a bottleneck for performance. However, the n-independent queues and relatively frequent work-stealing attempts will now become a bottleneck in FJ as there can be possibly many futile attempts to steal work which add to overhead.

In addition, the work-stealing algorithm in FJ assumes that (older) tasks stolen from the deque will produce enough parallel tasks to reduce the number of steals. E.g. in quicksort or mergesort where older tasks equate to larger arrays, these tasks will generate more tasks and keep the queue non-empty and reduce the number of overall steals. If this is not the case in a given application then the frequent steal attempts again become a bottleneck. This is also noted in the javadoc for ForkJoinPool:

this class provides status check methods (for example getStealCount()) that are intended to aid in developing, tuning, and monitoring fork/join applications.

这篇关于与Java-7 ForkJoinPool相比,Java-5 ThreadPoolExecutor有什么优势?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆