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

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

问题描述

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 集合 API).因此,在生成大量(通常运行时间相对较短)任务的场景中,FJ 执行器会表现得更好,因为独立队列将最小化并发操作,不频繁的窃取将有助于负载平衡.在 TP 中,由于队列单一,每次工作出队都会有并发操作,这会成为一个相对的瓶颈并限制性能.

相反,如果长时间运行的任务相对较少,那么 TP 中的单个队列不再是性能瓶颈.然而,n 个独立的队列和相对频繁的工作窃取尝试现在将成为 FJ 的瓶颈,因为可能会有许多徒劳的工作窃取尝试增加开销.

此外,FJ 中的工作窃取算法假设从双端队列窃取的(较旧的)任务将产生足够的并行任务以减少窃取次数.例如.在快速排序或归并排序中,较旧的任务等同于更大的数组,这些任务将生成更多任务并保持队列非空并减少整体窃取次数.如果在给定的应用程序中不是这种情况,那么频繁的窃取尝试再次成为瓶颈.这也在 ForkJoinPool 的 javadoc 中注明:><块引用>

该类提供状态检查方法(例如getStealCount())旨在帮助开发、调整和监控fork/join 应用程序.

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

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