JUC学习笔记-11-ForkJoin分支合并

11 分之合并

11.1 什么是ForkJoin

ForkJoin是一个并发执行任务的框架,其核心思想就是任务切分,结果合并。
当任务足够小的时候可以直接解决它,否则将任务切分成可以独立解决的部分,小的任务解决之后再将它们合并。
在这里插入图片描述

ForkJoin框架得到组成

  • ForkJoinTask
  • ForkJoinWorkerThread
  • ForkJoinPool

ForkJoin框架的特点

  • 工作窃取
    一些线程共同执行某个大任务的时候,已完成本线程工作的线程可以窃取其他线程未完成的任务,提高执行效率

11.2ForkJoinTask

该类是ForkJoinPool执行的任务,要使用ForkJoin框架,必须创建ForkJoinTask任务。该类提供了将任务分割成子任务的方法fork、等待子任务完成的方法join,通常情况下,我们将一个大的任务fork成两个小的任务,在通过join等待子任务完成合并结果。

ForkJoinTask是一个抽象类,它有两个子类:RecursiveTask和RecursiveAction,这两个类也是抽象类,其中RecursiveTask是带有返回值得到任务,RecursiveAction是没有返回值的任务。Recursive是递归的意思,从字面上便
可看出这些任务是可以分割成子任务递归完成的
在这里插入图片描述
ForkJoinTask实现了Future接口,说明它是一个可以异步获取结果的任务,同时该任务实现了序列化接口。

11.3 ForkJoinWorkerThread和工作窃取

该类是执行ForkJoinTask的线程,ForkJoin框架使用了线程维护一个双端队列,并实现了工作窃取(work-stealing)算法来提高并发效率。

线程每次从所维护队列的头部取任务执行,当队列中没有任务可执行时,该线程会去其他线程维护队列的尾部窃取任务执行。换句话说,对于本线程维护的队列,任务执行的顺序是LIFO(后进先出),窃取其他线程队列任务的执行顺序是FIFO(先进先出)。看下工作窃取示意图:
在这里插入图片描述

11.4 ForkJoinPool

ForkJoinPool是执行ForkJoinTask的线程池,它在内部维护了一个ForkJoinThread数组,这些线程就是ForkJoinPool管理的线程。类似于ThreadPoolExecutor,我们将任务提交到ForkJoinPool之后,它维护的线程就会执行我们提交的任务。不同的是,ForkJoinPool会通过工作窃取算法,让线程在完成本身任务之后帮其他线程执行任务, 这个调度是ForkJoinPool的核心功能。

11.5 如何使用ForkJoin

  1. 构建任务:继承ForkJoinTask或者其子类RecursiveTask
  2. 通过ForkJoinPool.execute(new 任务)

创建任务Task

/**
 * @author yangxj
 * @description 描述:递归任务,泛型是计算后返回的结果类型
 * @date 2020/2/24 15:56
 */
public class MyRecursiveTask extends RecursiveTask<Long> {
    private long start; //开始值
    private long end; //结束值
    private static final long temp = 10000L; //中间值

    public MyRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {

        if (end - start <= temp) {
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }else{
            //获取中间值
            long middle = (start + end)/2;
            /**
             * fork()会不断的循环
             */
            //第一个任务
            MyRecursiveTask rightTask = new MyRecursiveTask(start,middle);
            rightTask.fork();
            //第二个任务
            MyRecursiveTask leftTask = new MyRecursiveTask(middle+1, end);
            leftTask.fork();

            //合并结果
            return rightTask.join() + leftTask.join();
        }
    }
}

执行任务

public class ForkJoinTest {
    private static final long SUM = 20_0000_0000;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
        test2();
        test3();
    }

    /**
     * 使用普通方法
     */
    public static void test1() {
        long star = System.currentTimeMillis();
        long sum = 0L;
        for (long i = 1; i < SUM ; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(sum);
        System.out.println("时间:" + (end - star));
        System.out.println("----------------------");
    }
    /**
     * 使用ForkJoin 方法
     */
    public static void test2() throws ExecutionException, InterruptedException {
        long star = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new MyRecursiveTask(0L, SUM);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long along = submit.get();

        System.out.println(along);
        long end = System.currentTimeMillis();
        System.out.println("时间:" + (end - star));
        System.out.println("-----------");
    }
    /**
     * 使用 Stream 流计算
     */
    public static void test3() {
        long star = System.currentTimeMillis();

        long sum = LongStream.range(0L, 20_0000_0000L).parallel().reduce(0, Long::sum);
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("时间:" + (end - star));
        System.out.println("-----------");
    }
}

详细内容参考ForkJoin框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值