JUC-分支合并(ForkJoin)

1.1 什么是ForkJoin

从JDK1.7开始,Java提供Fork/Join框架用于并行执行任务,就是将一个大任务分割成若干小任务,最终汇总每个小任务的结果得到这个大任务的结果。
过程主要有两步:

  • 第一: 任务切割;
  • 第二 : 结果合并

1.2 ForkJoin特点

工作窃取(work-stealing): 是指某个线程从其他队列里窃取任务来执行。

就是一个工作线程下会维护一个包含多个子任务的双端队列。而对于每个工作线程来说,会从头部到尾部依次执行任务。这时,总会有一些线程执行的速度较快,很快就把所有任务消耗完了。

线程的任务窃取: 比如你和你的小伙伴在一起吃水果,你的那份吃完了,他那份没吃完,这个时候你偷偷的拿了他的一些水果吃了。存在执行2个任务的子线程,存在A,B两个WorkQueue在执行任务,A的任务执行完了,B的任务没执行完,那么A的WorkQueue就从B的WorkQueue的ForkJoinTask数组中拿走了一部分尾部的任务来执行,可以合理的提高运行和计算效率。

1.3 核心类

1.3.1 ForkJoinPool

WorkQueue是一个ForkJoinPool中的内部类,它是线程池中线程的工作队列的一个封装,支持任务窃取。
每个线程都有一个WorkQueue,而WorkQueue中有执行任务的线程(ForkJoinWorkerThreadowner),还有这个线程需要处理的任务(ForkJoinTask<?>[] array)。那么这个新提交的任务就是加到array中。

1.3.2 ForkJoinTask

ForkJoinTask代表运行在ForkJoinPool中的任务。

主要方法

  • fork() 在当前线程运行的线程池中安排一个异步执行。简单的理解就是再创建一个子任务。
  • join() 当任务完成的时候返回计算结果。
  • invoke() 开始执行任务,如果必要,等待计算完成。

子类: Recursive :递归

  • RecursiveAction 一个递归无结果的ForkJoinTask(没有返回值)
  • RecursiveTask 一个递归有结果的ForkJoinTask(有返回值)

10.4 代码示例

10.4.1 源码分析

10.4.2 代码验证

核心代码

package cn.guardwhy.forkJoin;

import java.util.concurrent.RecursiveTask;
/*
* 求和计算的任务!!
* 1.使用forkjoin
* 1.1forkjoinpool 通过它来执行。
* 1.2 计算任务ForkJoinPool.excute(ForkJoinTask task)
* 1.3 计算类要继承ForkJoinTask
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
    
    
    private Long start; // 起始值
    private Long end; // 结束值

    // 临界值
    private Long temp = 10000L;

    public ForkJoinDemo(Long start, Long end) {
    
    
        this.start = start;
        this.end = end;
    }

    // 计算方法
    @Override
    protected Long compute() {
    
    
        // 常规方式
        if((end - start) < temp){
    
    
            // 定义sum值
            Long sum = 0L;
            for (Long i = start; i <= end ; i++) {
    
    
                sum += i;
            }
            return sum;
        }else {
    
    
          // ForkJoin递归
            long middle = (start + end) / 2; // 中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            // 拆分任务,把任务压入线程队列
            task1.fork();
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
            // 拆分任务,把任务压入线程队列
            task2.fork();
            return task1.join() + task2.join();
        }
    }
}

测试代码

package cn.guardwhy.forkJoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class ForkJoinTest {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
         // test1();    // sum=500000000500000000 时间: 6235
         test2();    // sum=500000000500000000 时间: 6017
        // test03();   // sum=500000000500000000 时间: 102
    }

    // 1.普通方式
    public static void test1() {
    
    
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 1L; i <=10_0000_0000 ; i++) {
    
    
            sum += i;
        }
        Long end = System.currentTimeMillis();
        System.out.println("sum=" +sum+" 时间: " + (end-start));
    }

    // 2.使用ForkJoin方法
    private static void test2() throws ExecutionException, InterruptedException {
    
    
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
        // 提交任务
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();

        long end = System.currentTimeMillis();
        System.out.println("sum=" +sum+" 时间: " + (end-start));
    }

    // 3.链式编程
    private static void test03() {
    
    
        long start = System.currentTimeMillis();
        // Stream并行流
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum=" +sum+" 时间: " + (end-start));
    }

}

猜你喜欢

转载自blog.csdn.net/hxy1625309592/article/details/114149138