Java 5 以 Executor 框架的形式引入了对线程池异步任务执行的支持,其核心是 java.util.concurrent.ThreadPoolExecutor 实现的线程池。 Java 7 以 java.util.concurrent.ForkJoinPool 的形式添加了一个替代线程池。
查看它们各自的 API,ForkJoinPool 在标准场景中提供了 ThreadPoolExecutor 功能的超集(尽管严格来说 ThreadPoolExecutor 提供了比 ForkJoinPool 更多的调优机会)。除此之外,观察到 fork/join 任务似乎更快(可能是由于工作窃取调度程序),需要的线程肯定更少(由于非阻塞连接操作),人们可能会觉得 ThreadPoolExecutor 已被 ForkJoinPool 取代。
但这真的正确吗?我读过的所有 Material 似乎都总结出两种线程池之间相当模糊的区别:
- ForkJoinPool 适用于许多相关的、任务生成的、简短的、几乎不会阻塞(即计算密集型)任务
- ThreadPoolExecutor 用于少数、独立、外部生成、冗长、有时会阻塞的任务
这种区分是否正确?我们能说得更具体一点吗?
最佳答案
ThreadPool (TP) 和 ForkJoinPool (FJ) 针对不同的用例。主要区别在于不同执行器使用的队列数量决定哪种类型的问题更适合任一执行器。
FJ 执行器有 n 个(也称为并行度级别)单独的并发队列(deques),而 TP 执行器只有一个并发队列(这些队列/deques 可能是不遵循 JDK Collections API 的自定义实现)。因此,在您生成大量(通常是相对较短的运行时间)任务的场景中,FJ 执行器将执行得更好,因为独立队列将最大限度地减少并发操作,并且不频繁的窃取将有助于负载平衡。在TP中,由于单队列,每次出队时都会有并发操作,会成为相对的瓶颈,限制性能。
相比之下,如果长时间运行的任务相对较少,则 TP 中的单个队列不再是性能瓶颈。然而,n 独立队列和相对频繁的工作窃取尝试现在将成为 FJ 的瓶颈,因为可能会有许多徒劳的尝试窃取工作,这会增加开销。
此外,FJ 中的工作窃取算法假设从双端队列窃取的(较旧的)任务将产生足够的并行任务以减少窃取次数。例如。在旧任务等同于更大数组的快速排序或合并排序中,这些任务将生成更多任务并保持队列非空并减少总体窃取次数。如果在给定的应用程序中不是这种情况,那么频繁的窃取尝试再次成为瓶颈。这在 ForkJoinPool 的 javadoc 中也有说明。 :
this class provides status check methods (for example getStealCount()) that are intended to aid in developing, tuning, and monitoring fork/join applications.
关于java - Java-5 ThreadPoolExecutor 与 Java-7 ForkJoinPool 相比有什么优势?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9276807/