异步编程的难点
如何优雅地实现异步编程一直都是一个难题,异步编程的通常做法就是采用callback的方法,但是这种方法通常会把代码嵌套在正常流程的代码中,而且当有多层嵌套的时候代码更加难以维护。
另外还有一点,异步编程的异常处理也是难以维护,特别是在Java中,异步编程通常由新的线程完成,而子线程的异常是无法在父线程捕获的,那么对于异步执行结果的获取就需要付出更大的代价,比如通过:轮询、事件驱动等来完成。
CompletableFuture
初识CompletableFuture
在Java8中引入了CompletableFuture类,同时实现了Future接口和CompletionStage接口,提供了一套用于异步编程的Api接口并且提供了异步处理
CompletableFuture提供了许多异步编程的操作,可以说是Java中的Promise了,下面通过CompletableFuture来实现上面提到的例子:
String userInfo = CompletableFuture.supplyAsync(() -> login())
.thenApplyAsync(token -> userInfo(token))
.get();
System.out.println(userInfo);
CompletableFuture API
CompletableFuture方法很多,功能也很丰富,这里不一一说明,主要可以分为这几类来使用:
1.把CompletableFuture当Future使用
CompletableFuture实现了Future接口,也就是Future能做的CompletableFuture也同样能使用,加上complete和completeExceptionally方法可以控制结果的结束:
CompletableFuture<String> f = new CompletableFuture<>();
Executors.newSingleThreadExecutor().submit(()->{
f.complete("hello");
//f.completeExceptionally(new RuntimeException("error"));
});
String result = f.get();
System.out.println(result);
CompletableFuture API
CompletableFuture方法很多,功能也很丰富,这里不一一说明,主要可以分为这几类来使用:
1.把CompletableFuture当Future使用
CompletableFuture实现了Future接口,也就是Future能做的CompletableFuture也同样能使用,加上complete和completeExceptionally方法可以控制结果的结束:
CompletableFuture<String> f = new CompletableFuture<>();
Executors.newSingleThreadExecutor().submit(()->{
f.complete("hello");
//f.completeExceptionally(new RuntimeException("error"));
});
String result = f.get();
System.out.println(result);
可以通过CompletableFuture来控制多个异步操作同时执行
CompletableFuture<String> f = new CompletableFuture<>();
new Thread(() -> {
try {
System.out.println("thread1:" + f.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println("thread2:" + f.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}).start();
f.complete("hello");
异步操作
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
使用如下:
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
连续异步操作
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
使用如下:
CompletableFuture<Void> f = CompletableFuture
.supplyAsync(() -> "hello")
.thenApplyAsync(res -> res + " world!")
.thenAcceptAsync(System.out::println);
// wait for job done
f.get();
结果&异常处理
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
使用如下:
// 异常处理
CompletableFuture<Object> f = CompletableFuture.supplyAsync(() -> "Hello")
.thenApplyAsync(res -> res + "World")
.thenApplyAsync(res -> {
throw new RuntimeException("error");
})
.exceptionally(e -> {
//handle exception here
e.printStackTrace();
return null;
});
f.get();
// 执行结果处理
CompletableFuture<Object> f2 = CompletableFuture.supplyAsync(() -> "Hello")
.thenApplyAsync(res -> res + "World")
.thenApplyAsync(res -> {
throw new RuntimeException("error");
})
.handleAsync((res, err) -> {
if (err != null) {
//handle exception here
return null;
} else {
return res;
}
});
Object result = f2.get();
System.out.println(result);
并行执行异步操作并统一处理结果
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "hello");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "world");
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> "!");
// 使用allOf方法
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2, f3);
all.get();
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
// 结合StreamAPI
List<String> result = Stream.of(f1, f2, f3)
.map(CompletableFuture::join)
.collect(Collectors.toList());
System.out.println(result);