Java-5 ThreadPoolExecutor相对于Java-7 ForkJoinPool有什么优势?

Hol*_*ine 34 java parallel-processing threadpool threadpoolexecutor forkjoinpool

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

查看各自的API,ForkJoinPool在标准场景中提供了ThreadPoolExecutor功能的超集(虽然严格来说ThreadPoolExecutor提供了比ForkJoinPool更多的调优机会).除此之外,fork/join任务看起来更快(可能是因为工作窃取调度程序)的观察结果显然需要更少的线程(由于非阻塞连接操作),可能会让人觉得ThreadPoolExecutor已被取代ForkJoinPool.

但这真的是对的吗?我读过的所有材料似乎总结为两种类型的线程池之间相当模糊的区别:

  • ForkJoinPool适用于许多依赖的,任务生成的,简短的,几乎不会阻塞(即计算密集型)的任务
  • ThreadPoolExecutor用于少量,独立,外部生成,长期,有时阻塞的任务

这种区别是否正确?我们能说出更具体的内容吗?

sha*_*ams 15

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

FJ执行器具有n(即并行级别)单独的并发队列(deques),而TP执行器只有一个并发队列(这些队列/ deques可能是不遵循JDK Collections API的自定义实现).因此,在生成大量(通常是相对较短的运行)任务的情况下,FJ执行程序将表现更好,因为独立队列将最小化并发操作,并且不频繁的窃取将有助于负载平衡.在由于单个队列的TP中,每次工作出列时都会有并发操作,并且它将作为相对瓶颈并限制性能.

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

此外,FJ的工作窃取算法假设从双端队列被盗(旧的)任务将产生足够的并行任务,以减少抢断数.例如,在快速排序或归并其中年长的任务等同于更大的阵列,这些任务将产生更多的任务,并保持队列非空,降低整体抢断数.如果在给定的应用程序中不是这种情况,则频繁的窃取尝试再次成为瓶颈.这在ForkJoinPool的javadoc中也有提到:

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

  • 应该补充的是,很多人谈到的"deque"并不是[Deque](http://docs.oracle.com/javase/7/docs/api/java/util/Deque.html)界面的已知实现.这是一个数组.查看`ForkJoinWorkerThread`的源代码.他有一个包默认访问字段`ForkJoinTask <?> [] queue`. (3认同)

Sco*_*ion 11

推荐阅读http://gee.cs.oswego.edu/dl/jsr166/dist/docs/ 来自ForkJoinPool的文档:

ForkJoinPool与其他类型的ExecutorService的不同之处主要在于使用工作窃取:池中的所有线程都尝试查找和执行提交给池的任务和/或由其他活动任务创建的任务(如果不存在则最终阻止等待工作) .当大多数任务产生其他子任务时(如大多数ForkJoinTasks),以及从外部客户端向池提交许多小任务时,这可以实现高效处理.特别是在构造函数中将asyncMode设置为true时,ForkJoinPools也可能适用于从未加入的事件样式任务.

fork join框架对于并行执行很有用,而执行器服务允许并发执行,并且存在差异.看到这个这个.

fork join框架还允许工作窃取(使用Deque).

这篇文章很好读.

  • 也许你可以说那是什么区别.并行和并发之间的差异可能看起来很技术性,一些实际的例子会很有用. (2认同)