JDK8的CompletableFuture使用问题

发布于 2022-09-05 01:42:58 字数 934 浏览 16 评论 0

        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秒呢?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

吻风 2022-09-12 01:42:58

看了哈题主的疑问,个人觉得题主可能还没有真正理解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);
        

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

短叹 2022-09-12 01:42:58

CompletableFuture.supplyAsync的javadoc:

Returns a new CompletableFuture that is asynchronously completed by a task running in the ForkJoinPool.commonPool() with the value obtained by calling the given Supplier.

ForkJoinPool.commonPool()的javadoc:

Returns the common pool instance. This pool is statically constructed; its run state is unaffected by attempts to shutdown or shutdownNow. However this pool and any ongoing processing are automatically terminated upon program System.exit. Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.

如果你把最后的sleep改成ForkJoinPool.commonPool().awaitQuiescence(2, TimeUnit.SECONDS);也能达到你预期结果

秋叶绚丽 2022-09-12 01:42:58

搜索一下:守护线程
当线程中只剩下守护线程时JVM就会退出,反之还有任意一个用户线程在,JVM都不会退出。
我们可以猜测CompletableFuture.supplyAsync启动了一个守护线程,实际上CompletableFuture内部默认使用ForkJoinPool,该线程池初始化一个线程工厂类:

    defaultForkJoinWorkerThreadFactory = new DefaultForkJoinWorkerThreadFactory();

查看他的的实现,每次都是创建守护进程。至于为什么一定要主线程sleep就很好理解。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文