• 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*/
  • 相关阅读:
    单例模式的八种写法
    反射
    工厂模式
    Java内存分配、管理小结
    Java 反射(二)
    Java反射
    servlet的web-xml配置详解
    substr和substring的区别
    C/C++中extern关键字详解
    lua总则
  • 原文地址:https://www.cnblogs.com/turbo30/p/13688204.html
Copyright © 2020-2023  润新知