Java7中的ForkJoin并发框架初探(上)——需求背景和设计原理

最近事情较多,很久没发文章了。前面关于Java并发的文章中主要介绍了并发的概念、思想、JavaSE5中java.util.concurrent包中的工具类的使用和实现源码的分析。这篇咱们来简要了解一下JavaSE7中提供的一个新特性 —— Fork Join 框架java

0. 处理器发展和需求背景算法

回想一下并发开发的初衷,其实能够说是有两点,或者说能够从两个方面看。后端

  • 对于单核的处理器来讲,在进行IO操做等比较费时的操做进行时,若是执行任务的方式是单任务的,那么CPU将会“空转”,知道IO操做结束。若是有多任务的调度机制,则在一个任务不须要CPU支持的时候,CPU能够被调度处理其余任务。简单地讲,并发能够提升CPU计算资源的利用率。
  • 对于多核,或者多个计算资源的状况下,并发能够在某种程度上达到“并行”,即同时运行,缩短了任务完成的时间,提升了任务完成的效率。

咱们再来看一下处理器计算能力的发展(讲并发或者并行基本都要提到),Intel的创始人之一Gordon Moore曾经说过一句话,大概意思是:数据结构

当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增长一倍,性能也将提高一倍。并发

咱们能够这样理解,处理器的计算能力在必定意义上和芯片上集成的晶体管数量有关,而这项继承技术的发展史飞快的。可是,什么事情都是有一个极限的,提高计算性能仅仅靠增长晶体管数量提升处理器主频是不现实的,因而多核处理器的概念就出来了。框架

随着在硬件上多核处理器的发展和普遍使用,软件开发上的变革也在进行。简单来想,对于多个不相关的小任务来说,能够分派到不一样的处理器核心来进行处理。然而,对于一个比较大的任务,如何可以充分利用多核计算资源就是一个值得考虑的问题。工具

解决这个问题的办法就是“分而治之”,而Fork Join正式这样一种思路的产物。性能

1. Fork Join 的设计简介线程

看过《Introduction to Algorithms》(《算法导论》)的朋友们应该还记得,在讲到归并排序(Merge Sort)和快速排序的时候,有一种很简单又颇有效率的思路就是“分而治之”,即“分治法”。而Fork Join的思路也是同理,只不过划分以后的任务更适合分派给不一样的计算资源,能够并行的完成任务。设计

ForkJoin的任务分解和合并

ForkJoin的任务分解和合并

当计算分别完成以后,最后再合并回来。

简单来看,就是一个递归的分解和合并,直到任务小到能够接受的程度。

2. Fork Join 设计要点

Fork Join设计出来就是为了提升任务完成的效率,围绕这个目标,有一些要点是设计中须要考虑的,下面就给出一些要点。

  • 线程的管理和线程的单纯性。基于如上的设计思路,咱们能够看到子任务之间的相关性是相对比较简单的,能够并行处理。为了提升效率,并不须要重量级的线程结构和对应的线程维护,线程实现简单就好,知足需求便可,下降维护成本。
  • 队列机制,硬件支持必定是比较有限的,那么分解的任务应该用队列维护起来,一个好的队列设计是颇有必要的。
  • “工做窃取”,也就是设计论文原文中提到的 Work Stealing 。对于负载比较轻的线程,能够帮助负载较重的执行线程分担任务。

对于使用Fork Join的开发者来说,须要注意:

  • 可用线程数和硬件支持。线程这东西,也是有开销的东西,绝对不是越多越好,尤为在硬件基础有限的状况下。
  • 任务分解的粒度。和前者有关系,就是分解的任务,“小”到什么程度是能够接受的,不可再分。

3. Fork Join数据结构支持

按照如上设计,分解执行一个大的任务,Fork Join至少须要考虑以下一些数据结构。

  • 轻量级的线程结构。
  • 维护线程的线程池,负责线程的建立,数量维护和任务管理。
  • 维护任务,并支持Work Stealing的双端队列。以下图。

ForkJoin队列

支持ForkJoin任务维护的双端队列Deque

对于子任务的分解,能够从后端取出分解再放入,而对于WorkStealing则能够从头部取出,放入其余队列的尾部。

到此,本文仅仅是对Fork Join的大体设计思路作一个描述、勾勒。下一篇文章中会对JDK1.7中给出的实现做出分析。

4. Fork Join其它参考

Doug Lea的文章可参见这里:DougLea关于ForkJoin设计、实现和性能分析的文章原文

相关文章
相关标签/搜索