ThreadPoolExecutitor vs ForkJoinPool:窃取子任务



来自java文档,

ForkJoinPool与其他类型的ExecutorService的区别主要在于采用了工作窃取:池中的所有线程都试图找到并执行由其他活动任务创建的子任务(如果不存在,则最终阻止等待工作)。

当大多数任务生成其他子任务时(就像大多数ForkJoinTasks一样),这可以实现高效处理。在构造函数中将asyncMode设置为true时,ForkJoinPools可能也适用于从未联接的事件样式任务。

在经历了下面的ForkJoinPool示例之后,与ThreadPoolExecutor不同,我还没有看到设置队列大小的参数。我不知道ForkJoinPool是如何窃取机制的。

//creating the ThreadPoolExecutor
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, 
new ArrayBlockingQueue<Runnable>(3000), threadFactory, rejectionHandler);

假设我已经创建了具有10个线程的ThreadPoolExecutor,并且已经提交了3000个可调用任务。这些线程如何分担子任务的执行负载

对于相同的用例,ForkJoin池的行为有何不同

如果你提前有3000个任务,而它们不会产生其他任务,那么两者的行为不会有实质性的不同:有10个线程,一次运行10个任务,直到它们全部完成。

ForkJoinPool是为这样的情况而设计的,即您有一个或几个任务要开始,但这些任务知道如何将自己拆分为子任务。在这种情况下,ForkJoinPool进行了优化,允许任务检查处理线程的可用性,并适当地进行拆分。

ForkJoinPool中,有两种队列——池,一种是提交任务时基本使用的队列,另一种是特定于线程的队列(即每个线程一个)。从ForkJoinTask,您可以调用新任务(通常是问题的一部分)。

这些新任务不提供给池队列,而是提供给线程特定的队列。因此,它们优先于池一,就好像你在同一任务中完成了所有工作一样。此外,调用程序任务似乎因子任务完成而被阻止。

实际上,"阻塞时间"是用来消耗子任务的。当其中一个线程被工作淹没时,让其他线程"闲逛"是愚蠢的。所以,"偷工作"就发生了。

超越。为了提高效率,"偷工减料"会从相反的角度来承担任务。这大大减少了对队列写入的争用。

在效率方面,最好只将问题拆分为两个子任务,并让子任务一次又一次地拆分。即使你知道这个问题必须直接分成N部分。这是因为"工作窃取"需要对共享资源进行并发写入,所以要限制其激活和争用!

相关内容

  • 没有找到相关文章

最新更新