这篇文章介绍 Java 8 的 CompletionStage API和它的标准库的实现 CompletableFuture。API通过例子的方式演示了它的行为,每个例子演示一到两个行为。 既然CompletableFuture类实现了CompletionStage接口,首先我们需要理解这个接口的契约。它代表了一个特定的计算的阶段,可以同步或者异步的被完成。你可以把它看成一个计算流水线上的一个单元,最终会产生一个最终结果,这意味着几个CompletionStage可以串联起来,一个完成的阶段可以触发下一阶段的执行,接着触发下一次,接着…… 除了实现CompletionStage接口, CompletableFuture也实现了future接口, 代表一个未完成的异步事件。CompletableFuture提供了方法,能够显式地完成这个future,所以它叫CompletableFuture。 创建一个完成的CompletableFuture 最简单的例子就是使用一个预定义的结果创建一个完成的CompletableFuture,通常我们会在计算的开始阶段使用它。 getNow(null)方法在future完成的情况下会返回结果,就比如上面这个例子,否则返回null (传入的参数)。 运行一个简单的异步阶段 这个例子创建一个一个异步执行的阶段: 通过这个例子可以学到两件事情: CompletableFuture的方法如果以Async结尾,它会异步的执行(没有指定executor的情况下), 异步执行通过ForkJoinPool实现, 它使用守护线程去执行任务。注意这是CompletableFuture的特性, 其它CompletionStage可以override这个默认的行为。 在前一个阶段上应用函数 下面这个例子使用前面 #1 的完成的CompletableFuture, #1返回结果为字符串message,然后应用一个函数把它变成大写字母。 注意thenApply方法名称代表的行为。 then意味着这个阶段的动作发生当前的阶段正常完成之后。本例中,当前节点完成,返回字符串message。 Apply意味着返回的阶段将会对结果前一阶段的结果应用一个函数。 函数的执行会被阻塞,这意味着getNow()只有转大写操作被完成后才返回。 在前一个阶段上异步应用函数 通过调用异步方法(方法后边加Async后缀),串联起来的CompletableFuture可以异步地执行(使用ForkJoinPool.commonPool())。 getNow在cf.join()之前,所以与转大写操作没有明确的先后关系 使用定制的Executor在前一个阶段上异步应用函数 异步方法一个非常有用的特性就是能够提供一个Executor来异步地执行CompletableFuture。这个例子演示了如何使用一个固定大小的线程池来应用大写函数。 消费前一阶段的结果 如果下一阶段接收了当前阶段的结果,但是在计算的时候不需要返回值(它的返回类型是void), 那么它可以不应用一个函数,而是一个消费者, 调用方法也变成了thenAccept: 本例中消费者同步地执行,所以我们不需要在CompletableFuture调用join方法。 异步地消费前一阶段的结果 同样,可以使用thenAcceptAsync方法, 串联的CompletableFuture可以异步地执行。 完成计算异常 现在我们来看一下异步操作如何显式地返回异常,用来指示计算失败。我们简化这个例子,操作处理一个字符串,把它转换成答谢,我们模拟延迟一秒。 我们使用thenApplyAsync(Function, Executor)方法,第一个参数传入大写函数, executor是一个delayed executor,在执行前会延迟一秒。 让我们看一下细节。 首先我们创建了一个CompletableFuture, 完成后返回一个字符串message,接着我们调用thenApplyAsync方法,它返回一个CompletableFuture。这个方法在第一个函数完成后,异步地应用转大写字母函数。 这个例子还演示了如何通过delayedExecutor(timeout, timeUnit)延迟执行一个异步任务。 […]