开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
一、CompletableFuture介绍
CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。
CompletableFuture实现了俩接口。这个是Future的实现类。使用completionStage接口去支持完成时触发的函数和操作。
CompletionStage接口定义了任务编排的方法,执行某一阶段,可以向下执行后续阶段。异步执行的,默认线程池是ForkJoinPool.commonPool(),但为了业务之间互不影响,且便于定位问题,强烈推荐使用自定义线程池。
默认线程池如下:
// 根据commonPool的并行度来选择,而并行度的计算是在ForkJoinPool的静态代码段完成的
private static final boolean useCommonPool =
(ForkJoinPool.getCommonPoolParallelism() > 1);
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
二、CompletableFuture方法介绍
1、获取返回的结果
- get();
- getNow() 这个方法是执行这个方法的时候任务执行完了就返回任务的结果,如果任务没有执行完就返回你的入参
2、异步操作
- supply是持有返回值的:completetableFuture.supplyAsync
- run是void返回值:completetableFuture.runAsync
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
指定一个线程池:
ExecutorService executor = Executors.newCachedThreadPool();
CompletableFuture<Void> runFuture = CompletableFuture.runAsync(() -> System.out.println("run,关注"), executor);
public DeferredResult<Result> get() {
DeferredResult<Result> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(() -> {
return 阻塞数据库IO;
//dbThreadPool用来处理阻塞的数据库IO
}, dbThreadPool).thenComposeAsync(result -> {
deferredResult.setResult(result);
});
return deferredResult;
}
3、并行执行
- completetableFuture.allOf入参是一个completableFuture组、allOf就是所有任务都完成时返回。但是是个Void的返回值。
- completetableFuture.anyOf入参的completableFuture组中有一个任务执行完毕就返回。返回结果是第一个完成的任务的结果。
4、join
completetableFuture.join方法跟线程的join用法差不多,将主线程阻塞。
5、结果处理
completetableFuture.whenComplete 任务执行完毕调用的,传入一个action,这个方法的执行线程是当前线程,意味着会阻塞当前线程。 completetableFuture.whenCompleteAsync completetableFuture.whenCompleteAsync用自定义Executor
6、依赖关系,结果转换,将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果
completetableFuture.then方法瞅着挺多的。实际上就是异不异步和加不加自定义Executor. completetableFuture.thenCompose就是一个任务执行完之后可以用它的返回结果接着执行的方法。方法返回的是另一个你期盼泛型的结果
CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int number = new Random().nextInt(30);
System.out.println("第一次运算:" + number);
return number;
}
})
.thenCompose(new Function<Integer, CompletionStage<Integer>>() {
@Override
public CompletionStage<Integer> apply(Integer param) {
return CompletableFuture.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int number = param * 2;
System.out.println("第二次运算:" + number);
return number;
}
});
}
});
completetableFuture.thenCombine。这个combine的理解就是结合两个任务的结果。
CompletableFuture<Integer> future1 = CompletableFuture
.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int number = new Random().nextInt(10);
System.out.println("任务1结果:" + number);
return number;
}
});
CompletableFuture<Integer> future2 = CompletableFuture
.supplyAsync(new Supplier<Integer>() {
@Override
public Integer get() {
int number = new Random().nextInt(10);
System.out.println("任务2结果:" + number);
return number;
}
});
CompletableFuture<Integer> result = future1
.thenCombine(future2, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer x, Integer y) {
return x + y;
}
});
System.out.println("组合后结果:" + result.get());
completetableFuture.thenRun就是这个任务运行完,再运行下一个任务 completetableFuture.thenApply(Function);这样的就是有入参有返回值类型的。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int result = 100;
System.out.println("第一次运算:" + result);
return result;
}).thenApply(number -> {
int result = number * 3;
System.out.println("第二次运算:" + result);
return result;
});
completetableFuture.thenAccept(Consumer);这样的就是有入参,但是没有返回值的
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> {
int number = new Random().nextInt(10);
System.out.println("第一次运算:" + number);
return number;
}).thenAccept(number ->
System.out.println("第二次运算:" + number * 5));