Par*_*bay 52 java concurrency multithreading executorservice fork-join
我刚读完这篇文章:Java-5 ThreadPoolExecutor相对于Java-7 ForkJoinPool有什么优势?觉得答案不够直接.
您能用简单的语言和示例来解释,Java 7的Fork-Join框架与旧解决方案之间的权衡取舍是什么?
我还阅读了Google关于Java提示的#1热门提示:何时从javaworld.com 使用ForkJoinPool vs ExecutorService但文章没有回答标题问题时,它主要讨论api差异......
Jak*_*ski 47
Fork-join允许您轻松执行分而治之的作业,如果您想要执行它,则必须手动执行ExecutorService.在实践ExecutorService中,通常用于同时处理许多独立请求(也称为事务),并在您希望加速一个连贯的作业时使用fork-join.
Ste*_*n C 32
Fork-join特别适用于递归问题,其中任务涉及运行子任务然后处理结果.(这通常被称为"分而治之"......但这并未揭示出基本特征.)
如果你尝试使用传统的线程解决这样的递归问题(例如通过ExecutorService),你最终会遇到等待其他线程向其传递结果的线程.
另一方面,如果问题没有这些特性,那么使用fork-join没有任何实际好处.
Rav*_*abu 14
Java 8在Executors中提供了一个API
static ExecutorService newWorkStealingPool()
Run Code Online (Sandbox Code Playgroud)
使用所有可用处理器作为其目标并行度级别创建工作窃取线程池.
通过添加此API,Executors提供了不同类型的ExecutorService选项.
根据您的要求,您可以选择其中一个,或者您可以查找ThreadPoolExecutor,它可以更好地控制有界任务队列大小RejectedExecutionHandler机制.
static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用在共享的无界队列中运行的固定数量的线程.
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个线程池,可以调度命令在给定的延迟后运行,或者定期执行.
static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
创建一个根据需要创建新线程的线程池,但会在它们可用时重用先前构造的线程,并在需要时使用提供的ThreadFactory创建新线程.
static ExecutorService newWorkStealingPool(int parallelism)
创建一个线程池,该线程池维护足够的线程以支持给定的并行度级别,并且可以使用多个队列来减少争用.
这些API中的每一个都旨在满足您的应用程序的各自业务需求.使用哪一个将取决于您的用例要求.
例如
如果您想按照到达顺序处理所有提交的任务,请使用 newFixedThreadPool(1)
如果要优化递归任务的大计算性能,请使用ForkJoinPool或newWorkStealingPool
如果您想定期或在将来的某个时间执行某些任务,请使用 newScheduledThreadPool
看看一个更漂亮的文章通过PeterLawrey对ExecutorService用例.
相关SE问题:
java Fork/Join池,ExecutorService和CountDownLatch
Fork-Join 框架是 Executor 框架的扩展,专门用于解决递归多线程程序中的“等待”问题。事实上,新的 Fork-Join 框架类都是从 Executor 框架的现有类扩展而来的。
Fork-Join 框架有两个核心特征
如果并行处理的需求是严格递归的,则只能选择Fork-Join,否则无论是executor还是Fork-Join框架都可以,虽然Fork-Join可以说因为空闲线程更好地利用了资源从更繁忙的线程“窃取”一些任务。
Fork Join 是 ExecuterService 的一个实现。主要区别在于此实现创建了 DEQUE 工作池。任务从一侧插入但从任何一侧撤回。这意味着如果您创建了new ForkJoinPool()它,它将寻找可用的 CPU 并创建那么多的工作线程。然后它在每个线程上均匀地分配负载。但是如果一个线程运行缓慢而其他线程运行速度很快,它们就会从慢速线程中挑选任务。从背面。以下步骤将更好地说明窃取。
第一阶段(初始):
W1 -> 5,4,3,2,1
W2 -> 10,9,8,7,6
第 2 阶段:
W1 -> 5,4
W2 -> 10,9,8,7,
第 3 阶段:
W1 -> 10,5,4
W2 -> 9,8,7 ,
而 Executor 服务会创建请求数量的线程,并应用一个阻塞队列来存储所有剩余的等待任务。如果您使用了 cachedExecuterService,它将为每个作业创建单个线程,并且不会有等待队列。
Brian Goetz最好地描述了这种情况:https : //www.ibm.com/developerworks/library/j-jtp11137/index.html
使用常规线程池来实现fork-join也具有挑战性,因为fork-join任务的大部分生命都在等待其他任务。此行为是导致线程饥饿死锁的秘诀,除非精心选择参数以限制创建的任务数或池本身不受限制。常规线程池是为彼此独立的任务而设计的,并且在设计时还考虑了潜在的阻塞,粗粒度的任务-fork-join解决方案不会产生任何结果。
我建议阅读全文,因为它有一个很好的示例说明为什么要使用fork-join池。它是在ForkJoinPool正式发布之前编写的,因此coInvoke()他所指的方法成为invokeAll()。
| 归档时间: |
|
| 查看次数: |
27416 次 |
| 最近记录: |