关于CompletableFuture的一切,看这篇文章就够了

简介: 关于CompletableFuture的一切,看这篇文章就够了

文章目录



java中CompletableFuture的使用


之前的文章中,我们讲解了Future, 本文我们将会继续讲解java 8中引入的CompletableFuture的用法。


CompletableFuture首先是一个Future,它拥有Future所有的功能,包括获取异步执行结果,取消正在执行的任务等。


除此之外,CompletableFuture还是一个CompletionStage。


我们看下CompletableFuture的定义:


public class CompletableFuture<T> implements Future<T>, CompletionStage<T>


什么是CompletionStage呢?


在异步程序中,如果将每次的异步执行都看成是一个stage的话,我们通常很难控制异步程序的执行顺序,在javascript中,我们需要在回调中执行回调。这就会形成传说中的回调地狱。


好在在ES6中引入了promise的概念,可以将回调中的回调转写为链式调用,从而大大的提升了程序的可读性和可写性。


同样的在java中,我们使用CompletionStage来实现异步调用的链式操作。


CompletionStage定义了一系列的then*** 操作来实现这一功能。


CompletableFuture作为Future使用


调用CompletableFuture.complete方法可以立马返回结果,我们看下怎么使用这个方法来构建一个基本的Future:


public Future<String> calculateAsync() throws InterruptedException {
        CompletableFuture<String> completableFuture
                = new CompletableFuture<>();
        Executors.newCachedThreadPool().submit(() -> {
            Thread.sleep(500);
            completableFuture.complete("Hello");
            return null;
        });
        return completableFuture;
    }


上面我们通过调动ExecutorService来提交一个任务从而得到一个Future。如果你知道执行的结果,那么可以使用CompletableFuture的completedFuture方法来直接返回一个Future。


public Future<String> useCompletableFuture(){
        Future<String> completableFuture =
                CompletableFuture.completedFuture("Hello");
        return completableFuture;
    }


CompletableFuture还提供了一个cancel方法来立马取消任务的执行:


public Future<String> calculateAsyncWithCancellation() throws InterruptedException {
    CompletableFuture<String> completableFuture = new CompletableFuture<>();
    Executors.newCachedThreadPool().submit(() -> {
        Thread.sleep(500);
        completableFuture.cancel(false);
        return null;
    });
    return completableFuture;
    }


如果这个时候调用Future的get方法,将会报CancellationException异常。


Future<String> future = calculateAsyncWithCancellation();
future.get(); // CancellationException


异步执行code


CompletableFuture提供了runAsync和supplyAsync的方法,可以以异步的方式执行代码。


我们看一个runAsync的基本应用,接收一个Runnable参数:


public  void runAsync(){
        CompletableFuture<Void> runAsync= CompletableFuture.runAsync(()->{
            log.info("runAsync");
        });
    }


而supplyAsync接受一个Supplier:


public void supplyAsync(){
        CompletableFuture<String> supplyAsync=CompletableFuture.supplyAsync(()->{
            return "supplyAsync";
        });
    }


他们两个的区别是一个没有返回值,一个有返回值。


组合Futures


上面讲到CompletableFuture的一个重大作用就是将回调改为链式调用,从而将Futures组合起来。


而链式调用的返回值还是CompletableFuture,我们看一个thenCompose的例子:


CompletableFuture<String> completableFuture 
  = CompletableFuture.supplyAsync(() -> "Hello")
    .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));


thenCompose将前一个Future的返回结果作为后一个操作的输入。


如果我们想合并两个CompletableFuture的结果,则可以使用thenCombine:


public void thenCombine(){
        CompletableFuture<String> completableFuture
                = CompletableFuture.supplyAsync(() -> "Hello")
                .thenCombine(CompletableFuture.supplyAsync(
                        () -> " World"), (s1, s2) -> s1 + s2));
    }


如果你不想返回结果,则可以使用thenAcceptBoth:


public void thenAcceptBoth(){
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "Hello")
                .thenAcceptBoth(CompletableFuture.supplyAsync(() -> " World"),
                        (s1, s2) -> System.out.println(s1 + s2));
    }


thenApply() 和 thenCompose()的区别


thenApply()和thenCompose()两个方法都可以将CompletableFuture连接起来,但是两个有点不一样。


thenApply()接收的是前一个调用返回的结果,然后对该结果进行处理。


thenCompose()接收的是前一个调用的stage,返回flat之后的的CompletableFuture。

简单点比较,两者就像是map和flatMap的区别。


并行执行任务


当我们需要并行执行任务时,通常我们需要等待所有的任务都执行完毕再去处理其他的任务,那么我们可以用到CompletableFuture.allOf方法:


public void allOf(){
        CompletableFuture<String> future1
                = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> future2
                = CompletableFuture.supplyAsync(() -> "Beautiful");
        CompletableFuture<String> future3
                = CompletableFuture.supplyAsync(() -> "World");
        CompletableFuture<Void> combinedFuture
                = CompletableFuture.allOf(future1, future2, future3);
    }


allOf只保证task全都执行,而并没有返回值,如果希望带有返回值,我们可以使用join:


public void join(){
        CompletableFuture<String> future1
                = CompletableFuture.supplyAsync(() -> "Hello");
        CompletableFuture<String> future2
                = CompletableFuture.supplyAsync(() -> "Beautiful");
        CompletableFuture<String> future3
                = CompletableFuture.supplyAsync(() -> "World");
        String combined = Stream.of(future1, future2, future3)
                .map(CompletableFuture::join)
                .collect(Collectors.joining(" "));
    }


上面的程序将会返回:“Hello Beautiful World”。


异常处理


如果在链式调用的时候抛出异常,则可以在最后使用handle来接收:


public void handleError(){
        String name = null;
        CompletableFuture<String> completableFuture
                =  CompletableFuture.supplyAsync(() -> {
            if (name == null) {
                throw new RuntimeException("Computation error!");
            }
            return "Hello, " + name;
        }).handle((s, t) -> s != null ? s : "Hello, Stranger!");
    }


这和Promise中的catch方法使用类似。


本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/CompletableFuture

相关文章
|
8月前
|
存储 Java
「Java面试」工作3年竟然回答不出如何理解Reentrantlock实现原理
一个3 年工作经验的小伙伴,在面试的时候被这样一个问题。”谈谈你对ReentrantLock实现原理的理解“,他当时零零散散的说了一些。但好像没有说关键点。希望我分享一下我的理解。
68 0
Java 8 的异步利器:CompletableFuture源码解析(建议精读)
实现了俩接口,本身是个class。这个是Future的实现类,使用 completionStage 接口去支持完成时触发的函数和操作。
|
Java 开发者 Sentinel
CompletableFuture学习整理
整理的原因是在Sentinel源码中,我们可以看到很多关于CompletableFuture的thenCompose的源码。同时在业务系统里面也看到别人写过类似的代码。因此整理了一下关于CompletableFuture使用的相关类型和特性,在处理复杂耗时业务时可以选择组合使用。
163 0
CompletableFuture学习整理
|
存储 Java
【Java技术指南】「并发编程专题」CompletionService框架基本使用和原理探究(基础篇)
【Java技术指南】「并发编程专题」CompletionService框架基本使用和原理探究(基础篇)
138 0
|
Java
【Java原理探索】教你如何使用异步神器CompletableFuture| 8月更文挑战
【Java原理探索】教你如何使用异步神器CompletableFuture| 8月更文挑战
93 0
|
Web App开发 存储 移动开发
​Cookie 从入门到进阶:一文彻底弄懂其原理以及应用
​Cookie 从入门到进阶:一文彻底弄懂其原理以及应用
471 0
​Cookie 从入门到进阶:一文彻底弄懂其原理以及应用
|
存储 安全 算法
还怕被问到Java集合?看到这篇文章就够了!!!
还怕被问到Java集合?看到这篇文章就够了!!!
124 0
还怕被问到Java集合?看到这篇文章就够了!!!
|
监控 安全 Java
【精通函数式编程】(十一) CompletableFuture、反应式编程源码解析与实战
Future异步编程和CompletableFuture 接口都可以实现异步编程,我们通过源码深入理解其原理和设计的思想,Java9中提供了反应式编程(Flow API)我们分析其源码并提供一个响应式查询实战。
|
存储 Java 程序员
|
缓存 Java API
认真阅读完这篇文章熟练掌握高并发多线程——线程池
认真阅读完这篇文章熟练掌握高并发多线程——线程池
117 0
认真阅读完这篇文章熟练掌握高并发多线程——线程池