JUC高并发编程四

4 篇文章 0 订阅
4 篇文章 0 订阅

12、Fork/Join分支合并框架

1、简介

  1. ​ Fork/Join 他可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。Fork/Join 框架要完成两件事情

    Fork:把一个复杂的任务进行拆分,大事化小
    Join:把拆分的任务计算结果进行合并
    
    1、任务分割:首先Fork/Join框架需要把大的任务分割成足够小的任务,如果子任务比较大的话还要对子任务进行继续分割
    2、执行任务并合并结果:分割的自认分别放到双端队列里,然后启动几个线程分别从双端队列里获取任务执行。子任务执行完成的结果都会放到另外一个队列中里,启动一个线程从队列里获取结果,然后合并这些数据。
    
  2. 使用

    • ForkJoinTask:我们要使用Fork/Join框架,首先需要创建一个Fork/Join 任务,该类提供了在任务重执行fork和Join的机制,通常情况下我们不需要直接继承ForkJoinTask类,只需要继承他的子类,Fork/Join框架提供了两个子类

      类名描述
      RecursiveAction用于没有返回结果的任务
      RecursiveTask用于有返回结果的任务

      ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行

      RecursiveTask:继承后可以实现递归(自己调用自己)调用的任务。

    • Fork/Join 类关系图
      在这里插入图片描述

      分解fork
      分解fork
      分解fork
      分解fork
      分解fork
      分解fork
      汇总join
      汇总join
      汇总join
      汇总join
      汇总join
      汇总join
      大任务
      子任务1
      子任务1
      子任务1
      子任务2
      子任务1
      子任务2
      子任务1
      子任务1
      结果集汇总

2、代码实现

package com.codetip.codejuc.juc.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

class MyTask extends RecursiveTask<Integer> {
    // 查分差值不能超过是,10 以内的计算
    private static final Integer value = 10;
    // 拆分的开始值
    private int begin;
    // 拆分的结束值
    private int end;
    // 返回结果
    private int result;

    // 创建有参构造方法
    MyTask(int bengin, int end) {
        this.begin = bengin;
        this.end = end;
    }

    // 拆分和合并的过程
    @Override
    protected Integer compute() {
        // 判断开始和结束的值是否大于10 ,
        if ((end - begin) <= value) {
            for (int i = begin; i <= end; i++) {
                result = result + i;
            }
        } else {
            // 获取到中间值
            int middle = (begin + end) / 2;
            // 拆分左边
            MyTask task01 = new MyTask(begin, middle);
            // 拆分右边
            MyTask task02 = new MyTask(middle + 1, end);
            // 拆分完成
            task01.fork();
            task02.fork();
            // 合并结果
            result = task01.join() + task02.join();
        }
        return result;
    }
}

// ForkJoin代码演示
public class ForkJoinDemo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建MyTask对象
        MyTask task = new MyTask(0, 101);
        // 创建拆分合并池对象
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        // 提交任务
        ForkJoinTask<Integer> submit = forkJoinPool.submit(task);
        // 获取最终合并结果
        Integer result = submit.get();
        System.out.println(result);
    }
}

13、CompletableFutrue异步回调

1、简介

CompletableFuture 在 Java 里面被用于异步编程,异步通常意味着非阻塞,可以使得我们的任务单独运行在与主线程分离的其他线程中,并且通过回调可以在主线程中得到异步任务的执行状态,是否完成,和是否异常等信息。CompletableFuture 实现了 Future, CompletionStage 接口,实现了 Future接口就可以兼容现在有线程池框架,而 CompletionStage 接口才是异步编程的接口抽象,里面定义多种异步方法,通过这两者集合,从而打造出了强大的CompletableFuture 类

2、CompletableFuture与Future

Futrue 在 Java 里面,通常用来表示一个异步任务的引用,比如我们将任务提交到线程池里面,然后我们会得到一个 Futrue,在 Future 里面有 isDone 方法来 判断任务是否处理结束,还有 get 方法可以一直阻塞直到任务结束然后获取结果,但整体来说这种方式,还是同步的,因为需要客户端不断阻塞等待或者不断轮询才能知道任务是否完成

Future 的主要缺点如下

  • ​ 不支持手动完成

    我提交了一个任务,但是执行太慢了,我通过其他路径已经获取到了任务结果,现在没法把这个任务结果通知到正在执行的线程,所以必须主动取消或者一直等待它执行完成
    
  • 不支持进一步的非阻塞调用

    通过 Future 的 get 方法会一直阻塞到任务完成,但是想在获取任务之后执行额外的任务,因为 Future 不支持回调函数,所以无法实现这个功能
    
  • 不支持链式调用

    对于 Future 的执行结果,我们想继续传到下一个 Future 处理使用,从而形成一个链式的 pipline 调用,这在 Future 中是没法实现的
    
  • 不支持多个 Future 合并

    比如我们有 10 个 Future 并行执行,我们想在所有的 Future 运行完毕之后,执行某些函数,是没法通过 Future 实现的
    
  • 不支持异常处理

    Future 的 API 没有任何的异常处理的 api,所以在异步运行时,如果出了问题是不好定位的
    

3、使用ComplatetableFuture

  • 场景:主线程里面创建一个 CompletableFuture,然后主线程调用 get 方法会阻塞,最后我们在一个子线程中使其终止。

    代码案例

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class CompleteTableFutureDemo01 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            /* 主线程里面创建一个 CompletableFuture,然后主线程调用 get 方法会阻塞,最后我们在一个子线程中使其终止*/
            CompletableFuture<String> completableFuture = new CompletableFuture<>();
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "  子线程开始工作!!!");
                    //子线程睡 5 秒
                    TimeUnit.SECONDS.sleep(5);
                    //在子线程中完成主线程
                    completableFuture.complete("hello CompletableFuture");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "A").start();
            //主线程调用 get 方法阻塞
            //System.out.println(completableFuture.get());
            System.out.println("主线程完成,阻塞结束!!!!!!");
        }
    }
    
    
  • 没有返回值的异步任务

    • 代码演示
    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class CompleteTableFutureDemo02 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            System.out.println("主线程开始执行!!!!!!!!!!!!");
    
            CompletableFuture<Void> demo2 = CompletableFuture.runAsync(() -> {
                try {
                    System.out.println("子线程开始干活");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("子线程干活完成");
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            //主线程阻塞
            demo2.get(); // 调用get方法会阻塞主线程执行
            // 主线程继续执行任务
            System.out.println("主线程继续执行任务");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("主线程结束");
    
        }
    }
    
    
  • 有返回值的异步任务

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    
    public class CompleteTableFutureDemo03 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            System.out.println("主线程任务开始");
            CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println("子线程执行任务开始!!!");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("子线程执行任务结束!!!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "888";
            });
            // 主线程阻塞,等待获取子线程结果
            String result = completableFuture.get();  // 调用get方法会阻塞主线程执行
            System.out.println("主线程结束,子线程结果是:" + result);
        }
    }
    
    
  • 线程依赖

    当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化
    

    代码演示

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    public class CompleteTableFutureDemo04 {
        private static Integer num = 10;
    
        // 当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
                System.out.println("加10任务开始");
                return num += 10;
            }).thenApply(u -> u * u);  //  调度另外一个线程执行任务
            /*.thenApply(u->{
             return u * u;
             });
             */
            // 阻塞主线程等待获取结果
            Integer result = completableFuture.get();  // 调用get方法会阻塞主线程执行
            System.out.println("主线程执行结束,子线程的结果为:" + result);
    
        }
    }
    
    
    
  • 消费处理结果

    thenAccept 消费处理结果, 接收任务的处理结果,并消费处理,无返回结果
    

    代码演示:

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    public class CompleteTableFutureDemo05 {
        private static Integer num = 10;
    
        // 当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture.supplyAsync(() -> {
                System.out.println("加10任务开始");
                return num += 10;
            }).thenApply(u -> {
                System.out.println("求平方开始");
                return u * u;
            }).thenAccept(v -> System.out.println("子线程全部处理完成,最后调用了 accept,结果为:" + v));
        }
    }
    
    
  • 异常处理

    exceptionally 异常处理,出现异常时触发
    

    代码演示

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    public class CompleteTableFutureDemo06 {
        private static Integer num = 10;
    
        //exceptionally 异常处理,出现异常时触发
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
                System.out.println("加10任务开始");
                int i = 1 / 0;
                return num += 10;
            }).exceptionally((ex) -> {
                if (ex != null) {
                    System.out.println(ex.getMessage());
                    return -1;
                } else {
                    return num;
                }
            });
        }
    }
    
    
  • handle

    handle 类似于 thenAccept/thenRun 方法,是最后一步的处理调用,但是同时可以处理异常
    

    代码演示

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    public class CompleteTableFutureDemo07 {
        private static Integer num = 10;
    
        // handle 类似于 thenAccept/thenRun 方法,是最后一步的处理调用,但是同时可以处理异常
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
                int i = 1 / 0;// 模拟异常
                System.out.println("加10任务开始");
                return num += 10;
            }).handle((v, ex) -> {
                // 出现异常 返回 -1
                if (ex != null) {
                    return -1;
                } else {
                    // 没有异常 ,结果再加 5
                    return v + 5;
                }
            });
            // 主线程阻塞获取结果
            System.out.println(completableFuture.get());
    
        }
    }
    
    
  • 结果合并

  • thenCompose 合并两个有依赖关系的 CompletableFutures 的执行结果
    

代码演示

package com.codetip.codejuc.juc.completabkefutrue;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompleteTableFutureDemo08 {
    private static int num = 10;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("加10任务开始");
            return num += 10;
        });
        //合并
        CompletableFuture<Integer> completableFuture1 = completableFuture.thenCompose(i -> CompletableFuture.supplyAsync(() -> i + 3));

        System.out.println(completableFuture.get());
        System.out.println(completableFuture1.get());
    }
}

  • thenCombine 合并两个没有依赖关系的 CompletableFutures 任务
    

代码演示

package com.codetip.codejuc.juc.completabkefutrue;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
import java.util.function.Function;

public class CompleteTableFutureDemo09 {
    private static int num = 10;

    // thenCombine 合并两个没有依赖关系的 CompletableFutures 任务
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("加10任务开始");
            return num += 10;
        });
        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("乘10任务开始");
            return num * 10;
        });

        CompletableFuture<Object>  completableFuture3 = completableFuture1.thenCombine(completableFuture2, (a, b) -> {
            List<Integer> list = new ArrayList<>();
            list.add(a);
            list.add(b);
            return list;
        });

        System.out.println("合并的结果为: "+completableFuture3.get());
    }
}

  • 合并多个任务的结果 allOf 与 anyOf

    allOf: 一系列独立的 future 任务,等其所有的任务执行完后做一些事情
    

    代码演示

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    import java.util.stream.Collectors;
    
    public class CompleteTableFutureDemo10 {
        private static int num = 10;
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            System.out.println("主线程开始");
            List<CompletableFuture> list = new ArrayList<>();
            CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> {
                System.out.println("加 10 任务开始");
                num += 10;
                return num;
            });
            list.add(job1);
            CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> {
                System.out.println("乘以 10 任务开始");
                num = num * 10;
                return num;
            });
            list.add(job2);
            CompletableFuture<Integer> job3 = CompletableFuture.supplyAsync(() -> {
                System.out.println("减以 10 任务开始");
                num = num - 10;
                return num;
            });
            list.add(job3);
            CompletableFuture<Integer> job4 = CompletableFuture.supplyAsync(() -> {
                System.out.println("除以 10 任务开始");
                num = num / 10;
                return num;
            });
            list.add(job4);
            //多任务合并
            List<Integer> collect =
                    list.stream().map(CompletableFuture<Integer>::join).collect(Collectors.toList());
            System.out.println(collect);
        }
    }
    
    
    anyOf: 只要在多个 future 里面有一个返回,整个任务就可以结束,而不需要等到每一个future 结束
    

    代码演示

    package com.codetip.codejuc.juc.completabkefutrue;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    public class CompleteTableFutureDemo11 {
        private static int num = 10;
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            System.out.println("主线程开始");
            CompletableFuture<Integer>[] futures = new CompletableFuture[4];
            CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(5000);
                    System.out.println("加 10 任务开始");
                    num += 10;
                    return num;
                } catch (Exception e) {
                    return 0;
                }
            });
            futures[0] = job1;
            CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(2000);
                    System.out.println("乘以 10 任务开始");
                    num = num * 10;
                    return num;
                } catch (Exception e) {
                    return 1;
                }
            });
            futures[1] = job2;
            CompletableFuture<Integer> job3 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(3000);
                    System.out.println("减以 10 任务开始");
                    num = num - 10;
                    return num;
                } catch (Exception e) {
                    return 2;
                }
            });
            futures[2] = job3;
            CompletableFuture<Integer> job4 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(4000);
                    System.out.println("除以 10 任务开始");
                    num = num / 10;
                    return num;
                } catch (Exception e) {
                    return 3;
                }
            });
            futures[3] = job4;
            CompletableFuture<Object> future = CompletableFuture.anyOf(futures);
            System.out.println(future.get());
        }
    }
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值