分支/合并框架 ForkJoin

   分支/合并框架 是以递归的方式将可以并行的任务切割成更小的任务,然后将每个任务的结果进行合并来生成最终的结果。它是ExecutorService接口的实现,子任务在ForkJoinPool中执行。

要把任务提交到ForkJoinPool 可以通过继承两个类:一个是没有返回结果的 RecursiveAction,另一个RecursiveTask<R>有返回结果,并且R是返回结果的类型。如果要定义RecursiveTask,只要实现其唯一的抽象方法 compute。这个方法实现任务拆分的逻辑,以及拆分到最小后,每一个任务的处理逻辑。

任务的拆分如图

 

 

二 代码实例

 

 
public class MyForkAndJoinTest  extends RecursiveTask<Long> {



    private final long[] numbers;

    private final int start;

    private final int end;


   //阈值:拆分用
    public static final int IDEND = 10000;



    public MyForkAndJoinTest(long[] numbers){
        this(numbers,0,numbers.length);
    }

    public MyForkAndJoinTest(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    //重新方法:实现拆分逻辑,以及单个任务的逻辑
    @Override
    protected Long compute() {

        int le = end - start;
      if(le<=IDEND){
    //顺序执行任务
          return getSumFact();
      }

      MyForkAndJoinTest leftTask = new MyForkAndJoinTest(numbers,start,start+le/2);
      leftTask.fork();//将任务放入ForkJoinPool中,异步执行
      MyForkAndJoinTest rightTask = new MyForkAndJoinTest(numbers,le/2+start,end);
      Long result = rightTask.compute();//执行第二个任务
      Long leftR = leftTask.join();//读取结果
        return result+leftR;
    }


    public Long getSumFact(){

        Long sum = 0L;

        for (int i = start; i <end ; i++) {
            sum +=numbers[i];
        }
        return sum;
    }

    public static long  getSumToN(long n){
        long[] nums = LongStream.rangeClosed(0,n).toArray();
        ForkJoinTask<Long> tt = new MyForkAndJoinTest(nums);
        return new ForkJoinPool().invoke(tt);
    }

   
}

 

 

 

 

三 工作窃取机制

     分出大量的小任务一般来说都是一个好的选择。这是因为,理想情况下,划分并行任务应该让每个任务都用完全相同的时间完成,让所有的CPU 内核都同样繁忙。不幸的是,实际中个子任务所花的时间可能天差地别,要么是因为划分策略效率低,要么是有不可预知的原因,比磁盘访问慢,或是需要和外部服务协调执行。 

    分支/合并框架工程用一种称为工作窃取(work stealing)的技术来解决这个问题。在实际用中,这意味着这些任务差不多被平均分配到ForkJoinPool中的所有线程上。每个线程都为配给它的任务保存一个双向链式队列,每完成一个任务,就会从队列头上取出下一个任务开始行。基于前面所述的原因,某个线程可能早早完成了分配给它的所有任务,也就是它的队列已空了,而其他的线程还很忙。这时,这个线程并没有闲下来,而是随机选了一个别的线程,从列的尾巴上“偷走”一个任务。这个过程一直继续下去,直到所有的任务都执行完毕,所有的列都清空。这就是为什么要划成许多小任务而不是少数几个大任务,这有助于更好地在工作线之间平衡负载。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值