JUC学习之Fork/Join分支合并框架

1.什么是Fork/Join框架?
    Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

《JUC学习之Fork/Join分支合并框架》

2.Fork/Join框架的结构:需要ForkJoinPool才能使用
    在Fork/Join中,我们主要用它自定义的线程池来提交任务和调度任务,称之为:ForkJoinPool;同时我们有它自己的任务执行类,称之为:ForkJoinTask。通常情况下,我们不需要直接集成ForkJoinTask类,只需要继承它的子类。我们一般都使用它的两个子类,RecursiveAction和RecursiveTask,其中,前者主要处理没有返回结果的任务,后者主要处理有返回结果的任务。

3.Fork/Join框架的“工作窃取”模式
    什么是“工作窃取”呢?简单来说,就是说你和你的一个伙伴一起吃零食,你的那份吃完了,他那份没吃完,那你就偷偷的拿了他的一些水果吃了。
    原先的线程池,cpu给线程池中的线程分配任务,形成一个单端的任务队列,任务只能按一个顺序从队列中被领取,然后执行,这就可能导致,当有一个任务因为某些原因被阻塞的时候,队列中剩下的任务无法被执行。这就可能存在这样一个现象:线程池中一部分的线程完成了任务队列中的所有任务处于空闲状态,而有一部分线程因为任务阻塞无法继续。

    而Fork/Join提供的“工作窃取”模式可以有效解决上述的问题,任务队列是双端的,也就是说,其他线程在执行完自己队列中的任务后,可以从其他线程的任务队列的尾端获取任务来执行,从而提高cpu的利用效率。

举个例子:计算0到500000000的结果

public class TestForkJoinPool {

    public static void main(String[] args){
        Instant start = Instant.now();
        ForkJoinPool forkJoinPool = new ForkJoinPool();

        ForkJoinSum forkJoinSum = new ForkJoinSum(0L,500000000L);

        Long sum = forkJoinPool.invoke(forkJoinSum);

        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗费时间为:"+ Duration.between(start,end).toMillis());

    }
}

class ForkJoinSum extends RecursiveTask<Long>{

    private long start;
    private long end;
    private static final long THURSHOLD = 10000L;//设置临界值

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

    @Override
    protected Long compute() {
        long length = end - start;

        if(length <= THURSHOLD){
            long sum = 0L;

            for (long i = start; i <= end; i++) {
                sum += i;
            }

            return sum;
        }else{
            long middle = (start + end) / 2;

            ForkJoinSum left = new ForkJoinSum(start, middle);
            left.fork(); //进行拆分,同时压入线程队列

            ForkJoinSum right = new ForkJoinSum(middle+1, end);
            right.fork();

            return left.join() + right.join();
        }
    }
}

结果:

《JUC学习之Fork/Join分支合并框架》

如果把临界值改成50000L的结果:

《JUC学习之Fork/Join分支合并框架》

可以看出,不同的临界值,耗费的时间也是不同的,也就是说,有这么一个临界值,可以让耗费的时间达到最小值。而这个临界值就可以看做是任务的大小,在进入computer方法的时候,首先会判断,当前的任务是否足够小,如果足够小就执行任务;如果不是足够小,那就分割任务,就又会进入computer方法,看当前的任务是否需要继续分割子任务,以一种递归的方式进行分割。使用join方法会等待子任务执行完并得到其结果。

跟一般的for循环计算作对比:

@Test
    public void test1(){
        Instant start = Instant.now();
        Long sum = 0L;
        for (long i = 0; i <= 500000000L ; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start,end).toMillis());
    }

结果:

《JUC学习之Fork/Join分支合并框架》

跟java8 写法的对比:

@Test
    public void test2(){
        Instant start = Instant.now();
        Long sum = LongStream.rangeClosed(0L,500000000L).parallel().reduce(0L,Long::sum);
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+Duration.between(start,end).toMillis());
    }

结果:

《JUC学习之Fork/Join分支合并框架》

    原文作者:JUC
    原文地址: https://blog.csdn.net/dyt443733328/article/details/80064577
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞