Java并发JUC(七)

41 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

本文主要介绍并发中的ForkJoin和异步回调,ForkJoin会将任务拆分去做以提高效率,异步回调学过前端的朋友都应该很清楚。

14. ForkJoin

ForkJoin是在JDK1.7中出现的。并行执行任务!来提高效率。在大数据量大情况下它比较好。其实它做的一件事情就是将大任务划分成小任务去执行,最后合并。

大数据:Map Reduce(把大任务拆分成小任务)。

image.png ForkJoin特点:工作窃取。 就好比A、B两个线程去执行任务,B线程执行完了,B不会等待,会将A的任务拿过来一部分去执行。这里面维护的都是双端队列。

ForkJoin的操作

  • 第一步:新建ForkJoinPool,用ForkJoinPool来执行任务(我们用的是submit)。
  • 第二步:写一个继承ForkJoinTask的子类(也可以继承它的籽类),ForkJoinTask已知的子类:RecursiveTask(递归任务,有返回值的)或RecursiveAction(递归事件,无返回值的),函数内容写到compute方法。
  • 第三步:提交任务。
  • fork是拆分任务的方法,join是合并结果的方法。
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) {
            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);
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1,end);
            task1.fork();//拆分任务,把任务压入线程队列
            task2.fork();//拆分任务,把任务压入线程队列
            return task1.join() + task2.join();//任务结果合并
        }
    }
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务(有结果,可以拿到)
//        forkJoinPool.execute(task);//执行任务(没有结果)
        Long sum = submit.get();
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("sum =" + "时间:" + (end - start));
    }

15. 异步回调

Future设计的初衷,对将来的某个事件的结果进行建模。

这里的异步回调类似于前端的Ajax,发起一个异步请求,有成功回调,有失败回调。java中有个CompletableFuture是Future的实现类,用于做异步回调这件事。关于异步回调有两种情况:无返回值的异步回调;有返回值的异步回调。

针对无返回值的异步回调:

  • 异步任务发起的时候不会占用程序的执行时间,好比下面代码异步任务需要等待两秒,下面的结果1111会直接执行,不会等待异步任务执行完成再执行。
  • 发起一个请求 执行一个异步任务(参数是一个runnable(lambda表达式)) 异步任务发起的时候不会占用程序的执行时间
  • 用的是runAsync(),参数是一个runnable参数,function函数式接口。
运行结果:1111
         ForkJoinPool.commonPool-worker-1runAsync=>Void
public static void main(String[] args) throws ExecutionException, InterruptedException {
    //========================无返回值的异步回调================================//
    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
    });
    System.out.println("1111");
}

针对有返回值的异步回调:

  • 有返回值的话就是要么成功,要么失败。成功的话返回的是值,失败的话返回错误信息
  • 用的是supplyAsync(),参数是一个供给型函数,供给型函数没有参数,但是有返回值。
  • 值的获取用get方法,whenComplete方法获取执行结果,正常的话就返回结果t,失败的话就打印错误信息,可以返回错误码。具体使用如下述程序。
public static void main(String[] args) throws ExecutionException, InterruptedException {
    //========================有返回值的异步回调================================//
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
        System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");
        int i = 10/0;
        return 1024;
    });//这里是一个供给型的函数(没有参数)
    //下面可以获取到结果,成功的话t= 1024值就出来了,失败的话就打印u错误信息。返回错误码233
    System.out.println(completableFuture.whenComplete((t, u) -> {
        System.out.println("t=>" + t); //正常的返回结果
        System.out.println("u=>" + u); //u是错误信息
    }).exceptionally((e) -> {
        e.getMessage();
        return 233;  //获取到错误的返回结果
    }).get());
}