Java CompletableFuture使用
既然 CompletableFuture 类实现了 CompletionStage 接口,首先我们需要理解这个接口的契约。它代表了一个特定的计算的阶段,可以同步或者异步的被完成。你可以把它看成一个计算流水线上的一个单元,最终会产生一个最终结果,这意味着几个 CompletionStage 可以串联起来,一个完成的阶段可以触发下一阶段的执行,接着触发下一次,接着……
除了实现 CompletionStage 接口, CompletableFuture 也实现了 future 接口, 代表一个未完成的异步事件。CompletableFuture 提供了方法,能够显式地完成这个future,所以它叫 CompletableFuture 。
1、 创建一个完成的CompletableFuture
最简单的例子就是使用一个预定义的结果创建一个完成的CompletableFuture,通常我们会在计算的开始阶段使用它。
@Test
public void completedFutureExample() {
CompletableFuture cf = CompletableFuture.completedFuture("message");
assertTrue(cf.isDone());
assertEquals("message", cf.getNow(null));
}
getNow(null) 方法在future完成的情况下会返回结果,就比如上面这个例子,否则返回null (传入的参数)。
2、运行一个简单的异步阶段
@Test
public void runAsyncExample() {
CompletableFuture cf = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().isDaemon());
});
System.out.println(cf.isDone());
}
3、在前一个阶段上应用函数
下面这个例子使用前面 #1 的完成的CompletableFuture, #1返回结果为字符串 message ,然后应用一个函数把它变成大写字母。
then 意味着这个阶段的动作发生当在前一个阶段正常完成之后。本例中,当前节点完成,返回字符串 message 。
Apply 意味着返回的改阶段将会对前一阶段的结果应用一个函数。
函数的执行会被阻塞 ,这意味着 getNow() 只有打斜操作被完成后才返回。
@Test
public void thenApplyExample() {
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApply(s -> {
return s.toUpperCase();
});
System.out.println(cf.join());
}
4、在前一个阶段上异步应用函数
通过调用异步方法(方法后边加Async后缀),串联起来的CompletableFuture可以异步地执行(使用ForkJoinPool.commonPool())。
@Test
public void thenApplyAsyncExample() {
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
assertTrue(Thread.currentThread().isDaemon());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
return s.toUpperCase();
});
System.out.println(cf.join());
}
5、使用定制的Executor在前一个阶段上异步应用函数
异步方法一个非常有用的特性就是能够提供一个Executor来异步地执行CompletableFuture。这个例子演示了如何使用一个固定大小的线程池来应用大写函数。
@Test
public void thenApplyAsyncWithExecutorExample() {
ExecutorService executor = Executors.newFixedThreadPool(3, new ThreadFactory() {
int count = 1;
@Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable, "custom-executor-" + count++);
}
});
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
System.out.println((Thread.currentThread().getName()));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
return s.toUpperCase();
}, executor);
System.out.println(cf.join());
}
6、消费前一阶段的结果
如果下一阶段接收了当前阶段的结果,但是在计算的时候不需要返回值(它的返回类型是void), 那么它可以不应用一个函数,而是一个消费者, 调用方法也变成了 thenAccept
@Test
public void thenAcceptExample() {
StringBuilder result = new StringBuilder();
CompletableFuture.completedFuture("thenAccept message")
.thenAccept(s -> result.append(s));
System.out.println(result);
assertTrue("Result was empty", result.length() > 0);
}
7、异步地消费前一阶段的结果
@Test
public void thenAcceptAsyncExample() {
StringBuilder result = new StringBuilder();
CompletableFuture cf = CompletableFuture.completedFuture("thenAcceptAsync message")
.thenAcceptAsync(s -> result.append(s));
cf.join();
assertTrue("Result was empty", result.length() > 0);
}