解决的问题:上文介绍了future接口的使用和基本方法,我们留下了两个问题,想获取异步执行结果,直接使用可能会造成阻塞,使用轮训又会出现cpu消耗的问题,这就出现了cpmpaleteFuture,它可以在异步执行完以后,有特定的api在完成后去调用这个api,直接上代码
public static void main(String[] args) {
ExecutorService executorService=Executors.newFixedThreadPool(3);
//supplyAsync()可以定义返回值,现在使用的runAsync()由于传入的是Rnnable接口,是没有返回值的
CompletableFuture<Void> exceptionally = CompletableFuture.runAsync(() -> {
System.out.println("dfgdgfdj");
}, executorService).whenComplete((r, e) -> {
System.out.println("执行结果为"+r);
}).exceptionally(e -> {
e.printStackTrace();
System.out.println("ddsdsd");
return null;
});
TimeUnit.SECONDS.sleep(3);//线程阻塞的方法
}
注意:这里有一个问题,我在代码中使用了自己的线程池来操作异步任务,如果使用默认的线程池就会出现一个问题,在主线程任务执行完以后,如果异步线程执行任务还没执行完就会直接把线程清除掉,因为默认线程池中的都是守护线程forkjoinpool,当没有用户线程以后,会随着jvm一起清除,可以在主线程中阻塞几秒来解决,但是这样的编码显得格外的不优雅
completeFuture配合stream流的使用
使用场景:比如我需要同时去查询好几个数据吗,然后返回来进行操作,如果查了一个差查一个,就会比较麻烦,这时候就可以使用stream流进行多线程异步操作,然后统一返回结果进行操作
completeFuture相对于future的功能拓展
首先我们要知道compelateFuture继承了两个接口Future以及completeStage,所以它会比future具有更多的功能,常用的几个api我在这里介绍一下
1.get()
2.get(等待时间,时间单位)
3.getNow(completeFuture定义的泛型实例):这里在去调用这个方法的时候就会去判断异步任务是否已经执行完毕,如果执行完毕,就会把返回值返回,如果没有执行完毕,就会把自定义的返回值返回
4.complete(completeFuture定义的泛型实例):和上面的getnow类型,但是这个方法的返回值是boolean类型,它在使用时会判断是否执行完,没执行完就要返回自定义的实例,可以通过get或者是join方法获取,返回值也就是true,否则返回值就是false
5.其实就是get方法,但是get方法返回时需要抛出异常,join避免了这个问题
completeFuture之计算结果进行处理
- thenApply :对返回结果进行进一步处理,如果抛出异常则直接走到exceptionally这里
CompletableFuture<Integer> exceptionally = CompletableFuture.supplyAsync(() -> { return 1; }).thenApply(a -> { return a + 5; }).thenApply(x -> { return x + 7; }).exceptionally(a -> { return 8; }); }
- handle :和thenapply如出一辙,但是会连带着一个异常信息,且如果某个步骤出现异常,不会影响下边步骤的运行,下边的handle仍可继续运行,最后走到 exceptionally
CompletableFuture<Integer> exceptionally = CompletableFuture.supplyAsync(() -> { return 1; }).handle((a,e) -> { return a + 5; }).handle((a,e)-> { return a + 7; }).handle((a,e) -> { return 8; });
- thenRun:消费型函数,不接受任何参数,上一步执行完,继续往下执行
- thenAccept: 消费型函数,接受上一步的返回值,但自己本身无返回值
completeFuture对线程的选择
那为什么调用thenrun或者thenAsync会有这样的区别呢,其实很简单我们打开他们的源码会发现,thenAsync会先去拿到一个useCommonPool的常量,其实就是cpu的核数,判断是否大于一,相信现在的计算机一般都是大于一的吧,这里肯定为true,然后看下面
是不是瞬间就悟了, 使用thenAsync获取到的线程池总会是forkjoinpool
completeFuture计算速度选用
applyToEither:两个线程的运行速度进行对比,会把快的实例进行返回
实例1.applyToEither(实例2,更快的实例->{
})
completableFuture之计算结果合并
thenCombine
public static void main(String[] args) {
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
return 1;
});
CompletableFuture<Integer> integerCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
return 1;
});
CompletableFuture<String> stringCompletableFuture = integerCompletableFuture.thenCombine(integerCompletableFuture1, (x, y) -> {
return x + y + "rerere";
});
}