Java中的异步计算
异步计算很难推理。通常,我们希望将任何计算视为一系列步骤,但在异步计算的情况下,表示为回调的操作往往分散在代码中或彼此深度嵌套。当我们需要处理在其中一个步骤中可能发生的错误时,情况会变得更糟。
Future接口是在Java 5中添加的,作为异步计算的结果,但它没有任何方法来组合这些计算或处理可能的错误。
Java 8 引入了CompletableFuture类。除了Future接口之外,它还实现了CompletionStage接口。此接口定义了我们可以与其他步骤组合的异步计算步骤的协定。
CompletableFuture同时是一个构建块和一个框架,有大约50种不同的方法来组合,组合和执行异步计算步骤以及处理错误。
如此庞大的API可能会让人不知所措,但这些大多落在几个清晰而独特的用例中。
使用Completable作为一个简单的Future
首先,CompletableFuture 类实现了 Future 接口,因此我们可以将其用作 Future 实现,但具有额外的完成逻辑。
例如,我们可以创建一个无参构造函数的此类实例来表示将来的某个结果,将其分发给使用者,并在将来的某个时间使用完整方法完成它。使用者可以使用 get 方法阻止当前线程,直到提供此结果。
在下面的示例中,我们有一个创建CompletableFuture实例的方法,然后在另一个线程中分离出一些计算并立即返回Future。
计算完成后,该方法通过给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;
}
为了分拆计算,我们使用Executor API。这种创建和完成 CompletableFuture 的方法可与任何并发机制或 API(包括原始线程)一起使用。
请注意,calculateAsync 方法返回一个 Future 实例。
我们只需调用该方法,接收 Future 实例,并在准备好阻止结果时调用get方法。
还要观察 get 方法会引发一些已检查的异常,即 ExecutionException(封装计算期间发生的异常)和 InterruptedException(表示执行方法的线程被中断的异常):
Future<String> completableFuture = calculateAsync();
//……
String result = completableFuture.get();
System.out.println("hello".equals(result));
如果我们已经知道计算的结果,我们可以使用静态completedFuture 方法,并带有表示此计算结果的参数。因此,Future 的 get 方法永远不会阻塞,而是立即返回此结果:
CompletableFuture<String> completableFuture = CompletableFuture.completedFuture("hello");
//……
String result = completableFuture.get();
System.out.println("hello".equals(result));
作为替代方案,我们可能希望取消Future的执行。
具有封装计算逻辑的CompletableFuture
上面的代码允许我们选择任何并发执行的机制,但是如果我们想跳过这个样板并简单地异步执行一些代码呢?
静态方法runAsync和supplyAsync允许我们相应地从Runnable和Supplier 函数类型中创建CompletableFuture实例。
Runnable 和 Supplier 都是函数接口,由于采用了Java8新特性,它们允许将其实例作为 lambda 表达式传递。
Runnable 接口与线程中使用的旧接口相同,它不允许返回值。
Supplier 接口是一个泛型函数接口,具有单个方法,该方法没有参数,并返回参数化类型的值。
这允许我们提供Supplier的实例作为 lambda 表达式,用于执行计算并返回结果。它就像这样简单:
// 返回一个新的 CompletableFuture,它由在ForkJoinPool.commonPool()中运行的任务异步完成,其值通过调用给定的Supplier获得。
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello"