一分钟带你了解下java中的fork/join

前言

我们上小学的时候经常会遇到老师问这样的问题,就是1+2+……+99+100=?许多年前,有个叫高斯的小朋友也遇到了这个问题并很快得出了结果,今天我们利用java的分支、合并框架来计算下,原理和高斯小朋友的类似。

一、分支/合并框架简介

分支/合并框(Fork/Join)是Java7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。它使用工作窃取(work-stealing)算法,主要用于实现“分而治之”。

二、分支/合并流程图

用一张图展示下分支、合并流程:

三、涉及的Java类

JDK里面与fork/join相关的主要类如下:

ForkJoinPool

充当fork/join框架里面的管理者,最原始的任务都要交给它才能处理。它负责控制整个fork/join有多少个workerThread,workerThread的创建,激活都是由它来掌控。它还负责workQueue队列的创建和分配,每当创建一个workerThread,它负责分配相应的workQueue。然后它把接到的活都交给workerThread去处理,它可以说是整个frok/join的容器。

ForkJoinTask

代表fork/join里面任务类型,一般用它的两个子类RecursiveTask、RecursiveAction。这两个区别在于RecursiveTask任务是有返回值,RecursiveAction没有返回值。任务的处理逻辑包括任务的切分都集中在compute()方法里面。

fork/join主要类图:

四、代码实战

用代码实现下1到100自然数的求和。

伪代码如下:

if(任务足够小或不可分){

顺序计算该任务

}else{

将任务分成两个子任务

递归调用本方法,拆分每个子任务,等待所有子任务完成

合并每个子任务的结果

}

完整代码如下:

/**

*@Author:Java碎碎念

*/

publicclassCountTaskextendsRecursiveTask{

//阀值

privatestaticfinalintTHRESHOLD=2;

privateintstart;

privateintend;

publicCountTask(intstart,intend){

this.start=start;

this.end=end;

}

@Override

protectedIntegercompute(){

intsum=0;

//判断是否是拆分完毕

intlenth=end-start;

if(lenth<=THRESHOLD){

//如果拆分完毕就相加

for(inti=start;i<=end;i++){

sum+=i;

}

}else{

//没有拆分完毕就开始拆分

intmiddle=(start+end)/2;

CountTaskleftTask=newCountTask(start,middle);

CountTaskrightTask=newCountTask(middle+1,end);

//执行子任务

invokeAll(leftTask,rightTask);

//等待子任务执行完,并得到其结果

IntegerrightResult=rightTask.join();

IntegerleftResult=leftTask.join();

//合并子任务

sum=leftResult+rightResult;

}

returnsum;

}

publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{

ForkJoinPoolforkJoinPool=newForkJoinPool();

CountTaskcountTask=newCountTask(1,100);

ForkJoinTaskforkJoinTask=forkJoinPool.submit(countTask);

System.out.println(forkJoinTask.get());

}

}

运行完成后会打印出结果:5050;

五、注意事项

对一个任务调用join方***阻塞调用方,直到该任务运行完成。因此,需要在两个子任务的计算都开始之后再调用它。否则,使用该框架计算会比原始的顺序算法更慢更复杂,因为每个子任务都必须等待另一个子任务完成才能启动。

ForkJoin是通过多线程的方式进行处理任务,因此当数据量不是特别大的时候,没有必要使用ForkJoin。因为多线程会涉及到上下文的切换,当数据量不大的时候使用串行会比使用多线程快。

执行子任务时候要注意,使用invokeAll,不能分别对子任务调用fork。

//正确代码

invokeAll(leftTask,rightTask);

//错误代码

subtask1.fork();

subtask2.fork();

java中的分支合并框架介绍完成了,有问题欢迎留言沟通哦!

全部评论

相关推荐

尊尼获获:闺蜜在哪?
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务