JUC:ForkJoinPool类的理解与应用、CompletableFuture类的理解与应用

 

 

 

ForkJoinPool类 JDK 1.7

ForkJoin 并发执行任务,提高效率,在大数据量表现显著。最适合的是计算密集型的任务。

ForkJoin工作原理

  • 是将大量的数据分成多个子任务处理,然后合并。

在这里插入图片描述

ForkJoin特点

  • 工作窃取:该线程的任务执行完之后,就会去窃取其他线程没有执行完的任务,把任务拿到自己这里来执行,提高效率。
  • 用的双端队列

在这里插入图片描述

java.util.concurrent.ForkJoinPool类继承Executor。

 

常用方法

  • public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) 提交一个ForkJoinTask来执行。

ForkJoinTask是一个抽象类方法,如果要使用ForkJoinPool去执行代码,就得继承ForkJoinTask的子抽象类去实现,然后使用ForkJoinPool的submmit方法去使用

在这里插入图片描述

在这里插入图片描述

ForkJoinTask<V> 抽象类

  • public final ForkJoinTask <V> fork() 把拆分任务压入到线程中
  • public final V join() 返回计算结果

RecursiveAction 抽象类

  • protected abstract void compute()这个任务执行的主要计算。

RecursiveTask<V> 抽象类

  • protected abstract V compute() 这个任务执行的主要计算。

LongStream 接口

  • static LongStream range(long startInclusive,long endExclusive) 返回从startInclusive(含)至 endExclusive通过增量步骤 1的最终结果。 [start,end)

  • static LongStream range(long startInclusive,long endExclusive) 返回从startInclusive至 endExclusive(含)通过增量步骤 1的最终结果。 (start,end]

  • LongStream parallel() 使其为并行流

  • long reduce(long identity, LongBinaryOperator op) 合并流产生的元素,并产生单个值返回。

 

案例 使用ForkJoin,并行Stream计算大数据的和

Recursive 递归

parallel 平行

ForkJoinDemon.java

public class ForkJoinDemon extends RecursiveTask<Long> {
    private long start;
    private long end;

    private final long temp = 10000L; // 规定一个临界值

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

    // 递归合并
    @Override
    protected Long compute() {
        // 如果计算的两个值在临界值以内,就直接使用for循环计算
        if((end - start) < temp){
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }else {
            // 计算中间值,将其分为两个线程两个对象去进行计算,在new的新对象中,
            // 也会去判断是否在临界值以内,否则还会继续new一个新对象,进入新线程中计算
            long middle = (end + start)/2;
            ForkJoinDemon task1 = new ForkJoinDemon(start, middle);
            task1.fork(); // 把拆分任务压入到线程中
            ForkJoinDemon task2 = new ForkJoinDemon(middle + 1, end);
            task2.fork();
            return task1.join()+task2.join(); // 将两个任务的返回值,合并在一起
        }
    }
}

 

ForkJoinTest.java

public class ForkJoinTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // test01(); // 结果:500000000500000000  时间:723
         test02(); // 结果:500000000500000000  时间:641
        // test03(); // 结果:500000000500000000  时间:467
    }

    // 普通方法for循环计算
    public static void test01(){
        long start = System.currentTimeMillis();
        long sum = 0L;
        for (int i = 1; i <= 10_0000_0000L; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("结果:" + sum + "  时间:" + (end-start));
    }

    // 使用ForkJoin 并行计算
    public static void test02() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        long sum = 0L;

        ForkJoinPool joinPool = new ForkJoinPool();
        ForkJoinDemon joinDemon = new ForkJoinDemon(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = joinPool.submit(joinDemon);
        sum = submit.get();

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

    // 使用Stream流 并行计算
    public static void test03(){
        long start = System.currentTimeMillis();

        // 设置计算范围,增1计算,使用并行,将并行线程的值合并成一个值,返回
        long sum = LongStream.rangeClosed(0, 10_0000_0000L).parallel().reduce(0, (a, b) -> a + b);

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

 

 

CompletableFuture<T>类 异步回调 JDK1.8

java.util.concurrent.CompletableFuture<T>类,异步回调类。如同前端的Ajax。如在主线程new了该类,该类会创建一个新的线程去执行任务,并且主线程和该新线程互不影响。可同时进行。

构造方法

  • public CompletableFuture() 创建一个新的不完整的CompletableFuture

常用方法

  • public static CompletableFuture<Void> runAsync(Runnable runnable) 异步完成任务,并没有返回值
  • public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) 异步完成任务,有返回值
  • public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action) 当成功完成异步任务的回调函数。
  • public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn) 捕获异常,并由返回值。
public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 没有返回值
        CompletableFuture<Void> runAsync = 
            CompletableFuture.runAsync(() -> System.out.println("已执行runAsync 200"));
        runAsync.get(); // 获取结果值,如果一直执行不完,该方法就会被阻塞,一直等待去get

        // 有返回值
        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
            // System.out.println(19/0);
            return 200;
        });
        //System.out.println(supplyAsync.get());

        // 当成功完成
        Integer info = supplyAsync.whenComplete((t, e) -> {
            System.out.println(t); // 获得成功执行完成的返回值,如果执行出错为null
            System.out.println(e); // 如果执行不成功,获取异常信息,如果没有异常为null
        }).exceptionally((e) -> { // 如果没有异常不执行
            e.getMessage(); // 将执行失败的异常信息获取出来
            return 404; // 有返回值
        }).get();

        System.out.println(info);

    }
}
/* 输出:
已执行runAsync 200
200
null
200*/

 

posted @ 2020-06-14 17:07  张还行  阅读(533)  评论(0编辑  收藏  举报