java - JDK8的CompletableFuture使用问题

查看:196
本文介绍了java - JDK8的CompletableFuture使用问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

        CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("enter into completableFuture()");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("start to out of completableFuture()");
            return "a";
        });

        System.out.println("do something else");

        cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->
                System.out.println(v)
        );
        
        System.out.println("finalize...");
        
        //注释最后一行,无法得到预期结果
        //TimeUnit.SECONDS.sleep(10);

得到引结果为:

do something else
enter into completableFuture()
finalize...
start to out of completableFuture()
a b

以上代码如果注释掉最后一行,无法得到预期结果。

为什么一定要显式的让程序sleep10秒呢?

解决方案

看了哈题主的疑问,个人觉得题主可能还没有真正理解CompletableFuture这个类被设计出来的含义,所以对于它的用法只能说是跟着方法写出来了,但是可能没有用对哈,这里谈谈我对它的理解

说到CompletableFuture,这是Java8出来的新东西,看源码可以看到它是实现了Future接口的,而Future接口是从5就有的老接口了,也就是以前就已经有Future接口的相关功能了,所以CompletableFuture作为8出来的新实现,其意义想必是更方便完成Future接口的功能的,所以这里必须要先明白Future接口是啥意义呢?

Future接口我觉得是一种对于将来某个时刻会发生的结果进行的抽象,两个关键词,将来和结果
所以换句话说,一个Future就是一个值,但是这个值不能立马获得,是将来某个时刻才能获得

举个例子,在淘宝上下了单买了一个手机(phone),创建这个订单就是创建一个Future<Phone>,下了单后并不能马上拿到手机,得等物流按照你的订单地址把手机送到你手上,可能是两三天后才能收到,这里将来收到的手机就是一个Future<Phone>,所以你不能在马上下完单后就直接抱怨,为啥我现在拿不到手机啊,坑爹啊,其实不然,对于你而言,送手机这个过程是一个异步过程,你自己就是一个主线程,同理,你列举的代码CompletableFuture.supplyAsync也是创建了一个异步过程,当主线程执行完System.out.println("finalize...");后没有看到你想要的结果,你就说为啥非要等上10s才看得到预期的结果啊,其实买手机的过程也要等上2,3天才能拿到手机一样噻

举了这个例子了再来说说CompletableFuture到底有什么作用,其实题主例子中已经有所体现其中一个功能,就是以前的Future并不能做到将两个异步Future处理合并为一个(两个Future互相独立,但是第二个Future依赖于第一个Future的计算结果),但是CompletableFuture可以通过多种方法比如thenApplythenAccept等模拟出这样一个效果

所以题主写的例子中,开始应该写成CompletableFutur<String> cf1可能好点,表示cf1是一个将来会返回字符串的Future,同时这段代码

cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->System.out.println(v));

可能写成这样更好点,方便理解

CompletableFuture<Void> cf2 = cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->System.out.println(v));

虽然thenAccept没有返回值,但也可以用Void来占位,可以看到整个代码,题主想要实现这么一个功能,cf1是一个将来可能会获得一个字符串的CompletableFuture,将来获得的这个字符串要再拼上一个字符串 b,然后最后再打印出来,融合这两个将来操作之后获得了一个新的CompletableFuture cf2cf2是一个异步过程,所以要想在主线程结束前获取到结果,那就要在主线程中表示出你想要结果噻,所以还需要加一个代码,把

System.out.println("finalize...");

改成

System.out.println("finalize...");
cf2.join();

就可以啦,join方法是一个阻塞方法,在未获取到结果之前是会阻塞主线程的,这样你相当于告诉主线程你要获取到结果不然主线程不能结束

或者你可以调用cf2.get(2, TimeUnit.SECONDS);等两秒钟返回结果,若是2秒之内没有返回是会报错的

======================================根据评论的更新=====================================
默认线程池创建的线程为守护线程

采用自己创建的线程池来执行异步任务,那个2是线程池中线程的个数,最后把executorService传入到supplyAsync方法的第二个参数里

// 创建一个线程数量为2的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2, Thread::new);

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("enter into completableFuture()");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("start to out of completableFuture()");
            return "a";
        }, executorService);
        

再次执行你就可以看到你想要的结果了

这篇关于java - JDK8的CompletableFuture使用问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆