当一个任务在非常庞大的时候都会尝试拆分任务,而Java7中提供了一种任务拆分的框架ForkJoin。
ForkJoin介绍
ForkJoin框架是用来并行执行任务的框架,它可以把一个大任务分割成多个小任务,然后各个小任务去执行再把执行结果汇总在一起得到最终的结果,通过这种拆分实现多线程并行执行充分的利用CPU资源,提高程序的效率。
ForkJoin框架主要分成两个步骤:
Fork(任务分割):首先就是把一个大任务拆分成小任务,如果小任务还是太大还会继续拆分,知道足够小。
Join(合并结果):每个任务执行完成得到的结果进行汇总,返回最终结果。
任务的分割与合并主流程如下图:
几个关键类
ForkJoin框架需要两个定义去支撑,一个是任务,一个是任务管理,需要一个类来表示一个任务,还需要一个管理者来管理任务的执行。
ForkJoinTask来表示一个任务,ForkJoinTask是一个抽象类,它有两个子类RecursiveTask、RecursiveAction,RecursiveTask用在有返回结果的任务,RecursiveAction用在没有返回结果的任务。
ForkJoinPool作为任务的管理类,ForkJoinTask需要依赖它来执行任务,并且ForkJoinPool还支持任务窃取,因为ForkJoinTask分割出来的任务会放到当前工作线程维护的队列中,当工作线程中的队列都被完成后它会从其他工作线程的队列中获取任务来执行。任务窃取充分的利用了线程资源加快并行计算。
简单示例
首先来看有返回结果的示例,也就是RecursiveTask的使用,代码如下图:
主要流程是自定义一个继承至RecursiveTask,然后实现compute方法,在compute方法去判断任务是否需要再次拆分,拆分后如何组装结果。上面这段代码是实现从一个数到一个数叠加功能,功能比较简单,如果两个数相差太大则拆分任务,然后子任务调用fork方法把自己加到工作队列中去,调用join方法获取到结果,最后合并结果。
RecursiveAction的测试代码如下图:
RecursiveAction与RecursiveTask基本差不多,只不过RecursiveAction不用等待获取结果,如果想要获取结果可以通过调用ForkJoinPool的awaitTermination方法尝试等待一段时间获取结果。
总结
ForkJoin框架主要依赖ForkJoinTask来表示任务,而ForkJoinTask依赖ForkJoinPool来执行任务,ForkJoinPool会创建工作线程去执行任务,执行任务调用的是ForkJoinTask的子类RecursiveTask、RecursiveAction的compute方法。
compute方法需要我们自己实现,在compute方法中可以定义如何拆分方法与组装结果,在拆分成功后调用ForkJoinTask的fork方法可以把任务加载到当前工作线程的队列中。
工作线程执行完一个任务后会从队列中去获取任务继续执行,当队列中没有任务的时候则会从其他工作线程的队列中获取任务。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!