前言:
在上一次Java精通并发-Future模式示例剖析与源码详解中学习了Future模式,但是呢它有一个最大的缺点就是调用get()方法来获取其任务的结果时会阻塞,回忆一下:
而基于这一个缺点而言,很多的开源项目或者公司都提供了自己的一个解决方案来弥补Future的不足,使得任务执行时和结果获取时都不阻塞,但是随着jdk1.8的发布,对于Future的进一步增强也已经问世了,它就是本次咱们要来研究的CompletableFuture,瞅一下:
了解CompletableFuture:
先来看一下它的定义:
咱们先简单了解一下CompletionStage:
简单来说CompletionStage它表示的是一个任务可能会分成多个阶段,当第一个阶段完成之后紧接着会触发第二个阶段的执行,当第二个阶段执行完之后则又触发第三个阶段的执行,以此类推,所以每一个阶段就称为CompletionStage,所以说CompletableFuture也能完成按阶段的处理任务。
接下来咱们再来简单的了解一下CompletableFuture:
这是啥意思呢?这里理解一下:对于上一次咱们学习的FutureTask来说当任务执行时我们是无法对它进行一些操作的,但是实际可能需要在任务执行过程中来进行状态的干预,比如说运行过程中强制要让它执行完成,对于CompletableFuture就能达成这点。
继续往下:
所以简单总结一下CompletableFuture它既支持Future的功能,比如最典型的get():
此外还提供有Future增强的功能,最典型的就是可以异步地执行和异步地获取结果,接下来咱们则来撸码看一下CompletableFuture是怎么用的。
实践:
示例一:对结果进行变换supplyAsync:
先直接上代码:
其实功能有点像Java8中的map操作符,那咱们简单分析一下:
其中对于Supplier它是一个Java8的函数式接口,对于熟悉Java8的来说应该比较熟了,这里简单看一下它的定义原型:
所以咱们使用的Lambda表达式的方式来写的,也就是这时会异步的执行一个任务,接着继续:
从这个方法名就知道是接着之前异步任务的执行继续再执行一个异步任务:
又是接收一个Lambda表达式,那这个例子貌似没有Future的功能呀,是的!!它其实展示的是这个功能:
也就是任务分阶段完成,另外CompletableFuture还提供另一种不返回结果的分阶段执行的API:
所以如果在执行任务时不需要返回结果则可以选择用runAsync,而如果需要返回结果则可以选择applyAsync。
示例二:thenAccept
先来看代码:
也就是说对于CompletableFuture第二种用法就是可以对结果做消费处理,而不返回结果。
示例三:对多个CompletionStage的结果进行转换
下面看代码:
package com.javacurrency.test7;
import java.util.concurrent.CompletableFuture;
public class MyTest2 {
public static void main(String[] args) {
String result2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "world";
}), (s1, s2) -> s1 + " " + s2).join();
System.out.println(result2);
}
}
运行看一下结果:
结果比较简单,重点是分析其过程:
然后:
对于这个combine如果熟悉Java8也是比较好懂的,因为也有类似的操作符,总的来说还是比较好理解的。
示例四:Future的增强,核心!!
如开篇所说,对于Future它有一个最大的毛病就是调用get()方法会阻塞,而CompletableFuture又说是Future的增强,解决了任务既能异步执行,其获取结果也不会阻塞,那么如何来实现呢?下面看一下:
package com.javacurrency.test7;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class MyTest2 {
public static void main(String[] args) {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task finished");
});
completableFuture.whenComplete((t, action) -> System.out.println("执行完成!"));
System.out.println("主线程执行完毕");
try {
TimeUnit.MILLISECONDS.sleep(7000);//让主线程阻塞的目的是为了能看到completableFuture的效果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行:
咱们来分析一下:
通过这个例子能感受到CompletableFuture和Future的不同。
关注个人公众号,获得实时推送