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秒呢?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
看了哈题主的疑问,个人觉得题主可能还没有真正理解
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
可以通过多种方法比如thenApply
,thenAccept
等模拟出这样一个效果所以题主写的例子中,开始应该写成
CompletableFutur<String> cf1
可能好点,表示cf1
是一个将来会返回字符串的Future
,同时这段代码可能写成这样更好点,方便理解
虽然
thenAccept
没有返回值,但也可以用Void
来占位,可以看到整个代码,题主想要实现这么一个功能,cf1
是一个将来可能会获得一个字符串的CompletableFuture
,将来获得的这个字符串要再拼上一个字符串b
,然后最后再打印出来,融合这两个将来操作之后获得了一个新的CompletableFuture cf2
,cf2
是一个异步过程,所以要想在主线程结束前获取到结果,那就要在主线程中表示出你想要结果噻,所以还需要加一个代码,把改成
就可以啦,
join
方法是一个阻塞方法,在未获取到结果之前是会阻塞主线程的,这样你相当于告诉主线程你要获取到结果不然主线程不能结束或者你可以调用
cf2.get(2, TimeUnit.SECONDS);
等两秒钟返回结果,若是2秒之内没有返回是会报错的======================================根据评论的更新=====================================
默认线程池创建的线程为守护线程
采用自己创建的线程池来执行异步任务,那个2是线程池中线程的个数,最后把
executorService
传入到supplyAsync
方法的第二个参数里再次执行你就可以看到你想要的结果了
见
CompletableFuture.supplyAsync
的javadoc:而
ForkJoinPool.commonPool()
的javadoc:如果你把最后的
sleep
改成ForkJoinPool.commonPool().awaitQuiescence(2, TimeUnit.SECONDS);
也能达到你预期结果搜索一下:守护线程
当线程中只剩下守护线程时JVM就会退出,反之还有任意一个用户线程在,JVM都不会退出。
我们可以猜测CompletableFuture.supplyAsync启动了一个守护线程,实际上CompletableFuture内部默认使用ForkJoinPool,该线程池初始化一个线程工厂类:
查看他的的实现,每次都是创建守护进程。至于为什么一定要主线程sleep就很好理解。