Java 5引入了对线程池执行异步任务的支持,该线程池的形式是Executor框架,其核心是由java.util.concurrent.ThreadPoolExecutor实现的线程池。Java 7以java.util.concurrent.ForkJoinPool的形式添加了另一个线程池。
从它们各自的API来看,ForkJoinPool在标准场景中提供了ThreadPoolExecutor功能的超集(尽管严格地说,ThreadPoolExecutor提供了比ForkJoinPool更多的调优机会)。更重要的是,如果观察到叉/连接任务似乎更快(可能是因为工作窃取调度程序),需要的线程绝对更少(由于非阻塞连接操作),人们可能会有ThreadPoolExecutor被ForkJoinPool取代的印象。
但这真的正确吗?我所读到的所有材料似乎总结了两种类型的线程池之间相当模糊的区别:
这个区别是正确的吗?关于这件事我们能说更具体点吗?
发布于 2012-04-28 18:39:56
ThreadPool (TP)和ForkJoinPool (FJ)针对不同的用例。主要的区别在于不同的执行者所使用的队列数量,这决定了哪种类型的问题更适合于任何一个执行器。
FJ执行器有n个(也称为并行级别)独立的并发队列(deques),而TP执行器只有一个并发队列(这些队列/deques可能是不遵循JDK集合API的自定义实现)。因此,在生成大量(通常相对较短)任务的情况下,FJ执行器的性能将更好,因为独立队列将最小化并发操作,而不频繁的抢断将有助于负载平衡。在TP中,由于单个队列的存在,每次工作脱离队列时都会出现并发操作,这将成为一个相对的瓶颈,限制了性能。
相反,如果长时间运行的任务相对较少,则TP中的单个队列不再是性能的瓶颈。然而,独立于n的队列和相对频繁的偷工作尝试现在将成为FJ的瓶颈,因为可能会有许多徒劳无益的窃取工作的企图增加了开销。
另外,FJ中的偷工作算法假设从deque窃取的(旧的)任务会产生足够多的并行任务,从而减少偷取的次数。例如,在较早的任务等同于较大数组的快速排序或合并中,这些任务将生成更多的任务,使队列保持非空,并减少整体抢占的数量。如果在给定的应用程序中不是这样,那么频繁的偷取尝试再次成为瓶颈。这在javadoc for ForkJoinPool中也有说明。
这个类提供了状态检查方法(例如,getStealCount()),这些方法旨在帮助开发、优化和监视叉/连接应用程序。
发布于 2012-02-14 12:29:40
建议从文档中读取http://gee.cs.oswego.edu/dl/jsr166/dist/docs/以获取ForkJoinPool:
ForkJoinPool不同于其他类型的ExecutorService,主要是因为使用了窃取工作的方法:池中的所有线程都试图查找和执行提交给池的任务和/或其他活动任务创建的任务(如果不存在,最终阻塞等待工作)。当大多数任务产生其他子任务(就像大多数ForkJoinTasks一样)时,以及当许多小任务从外部客户端提交到池时,这就启用了高效的处理。特别是在构造函数中将asyncMode设置为true时,ForkJoinPools也可能适合用于从未加入的事件样式任务。
叉连接框架对于并行执行非常有用,而executor服务则允许并发执行,并且有一个不同之处。见这和这。
叉连接框架还允许窃取工作(使用Deque)。
这篇文章是一个很好的读物。
发布于 2012-02-14 12:30:21
AFAIK,如果你想要一件大的作品,并且你想要它自动分解,ForkJoinPool的工作效果最好。如果您知道如何将工作分解,ThreadPoolExecutor是一个更好的选择。由于这个原因,我倾向于使用后者,因为我已经决定了我想要如何分解工作。因此,并不是每个人都这样。
当涉及到相对随机的业务逻辑片段时,ThreadPoolExecutor将完成您需要的所有事情,这是没有价值的,那么为什么要使它变得比您需要的更复杂呢?
https://stackoverflow.com/questions/9276807
复制相似问题