转载自:http://www.jb51.net/article/51163.htm
static CompletableFuture supplyAsync(Supplier supplier);
static CompletableFuture supplyAsync(Supplier supplier, Executor executor);
static CompletableFuture runAsync(Runnable runnable);
static CompletableFuture runAsync(Runnable runnable, Executor executor);
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture f1 = CompletableFuture.supplyAsync(() -> {
return "zero";
}, executor);
CompletableFuture f2 = f1.thenApply(new Function() {
@Override
public Integer apply(String t) {
System.out.println(t);
return Integer.valueOf(t.length());
}
});
CompletableFuture f3 = f2.thenApply(r -> r * 2.0);
System.out.println(f3.get());
运行结果:
zero
8.0
注意,不是以Async结尾的方法将在future完成的相同线程中调用该方法中的参数,而以Async结尾的方法将在不同的线程池中异步地调用方法中的参数。
CompletableFuture thenAccept(Consumer super T> block);
CompletableFuture thenRun(Runnable action);
在future的管道里有两种典型的“最终”阶段方法,可以理解为回调函数。…Async变量也可用两种方法,隐式和显式执行器,thenAccept()/thenRun()方法并没有发生阻塞(即使没有明确的executor)。它们像一个事件侦听器/处理程序。
CompletableFuture safe = future.exceptionally(ex -> "We have a problem: " + ex.getMessage());
exceptionally()接受一个函数时,将调用原始future来抛出一个异常。这里会有机会将此异常转换为和Future类型的兼容的一些值来进行恢复。safe进一步的转换将不再产生一个异常而是从提供功能的函数返回一个String值。一个更加灵活的方法是handle()接受一个函数,它接收正确的结果或异常:
CompletableFuture future = f2.thenApply(r -> r * 2.0);
future.handle(new BiFunction(){
@Override
public Double apply(Double t, Throwable u) {
if (t != null) {
System.out.println("handler");
return t;
}else {
System.out.println(u);
return -1.0;
}
}
}
handle()总是被调用,结果和异常都非空,这是个一站式全方位的策略。
CompletableFuture thenCompose(Function super T, Function super T, ? extends CompletionStage> fn);//(CompletableFuture extends CompletionStage)
public static void f5() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture f1 = CompletableFuture.supplyAsync(() -> {
return "zero";
}, executor);
CompletableFuture> f4 = f1.thenApply(CompletableFutureTest::calculate);
System.out.println(f4.get().get());
CompletableFuture f5 = f1.thenCompose(CompletableFutureTest::calculate);
System.out.println(f5.get());
System.out.println(f1.get());
}
public static CompletableFuture calculate(String input) {
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println(input);
return input + "---" + input.length();
}, executor);
return future;
}
仔细观察thenApply()(map)和thenCompose()(flatMap)的类型和差异.thenCompose()是一个重要的方法允许构建健壮的和异步的管道,没有阻塞和等待的中间步骤。
public CompletableFuture thenCombine(
CompletionStage extends U> other,
BiFunction super T,? super U,? extends V> fn)
假设有两个CompletableFuture,一个加载Customer另一个加载最近的Shop。他们彼此完全独立,但是当他们完成时,想要使用它们的值来计算Route。这是一个可剥夺的例子:
CompletableFuture customerFuture = loadCustomerDetails(123);
CompletableFuture shopFuture = closestShop();
CompletableFuture routeFuture =
customerFuture.thenCombine(shopFuture, (cust, shop) -> findRoute(cust, shop));
private Route findRoute(Customer customer, Shop shop) //...
注意,在Java 8中可以用(cust, shop) -> findRoute(cust, shop)简单地代替this::findRoute方法的引用:
customerFuture.thenCombine(shopFuture, this::findRoute);
有customerFuture 和 shopFuture。那么routeFuture包装它们然后“等待”它们完成。当他们准备好了,它会运行提供的函数来结合所有的结果 (findRoute())。当两个基本的futures完成并且 findRoute()也完成时,这样routeFuture将会完成。
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture f1 = CompletableFuture.supplyAsync(() -> {
return "zero";
}, executor);
CompletableFuture f2 = CompletableFuture.supplyAsync(() -> {
return "hello";
}, executor);
CompletableFuture reslutFuture =
f1.thenCombine(f2, new BiFunction() {
@Override
public String apply(String t, String u) {
return t.concat(u);
}
});
System.out.println(reslutFuture.get());//zerohello
public CompletableFuture thenAcceptBoth(
CompletionStage extends U> other,
BiConsumer super T, ? super U> action)
public CompletableFuture runAfterBoth(CompletionStage> other,
Runnable action)
示例:
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture f1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "zero";
}, executor);
CompletableFuture f2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "hello";
}, executor);
CompletableFuture reslutFuture = f1.thenAcceptBoth(f2, new BiConsumer() {
@Override
public void accept(String t, String u) {
System.out.println(t + " over");
System.out.println(u + " over");
}
});
System.out.println(reslutFuture.get());
运行结果:
zero over
hello over
null
好了,你当然可以这么做。但是最关键的一点是CompletableFuture是允许异步的,它是事件驱动的编程模型而不是阻塞并急切地等待着结果。所以在功能上,上面两部分代码是等价的,但后者没有必要占用一个线程来执行。
public CompletableFuture acceptEither(CompletionStage extends T> other,
Consumer super T> action)
public CompletableFuture runAfterEither(CompletionStage> other,
Runnable action)
作为一个例子,当有两个系统可以集成。一个具有较小的平均响应时间但是拥有高的标准差,另一个一般情况下较慢,但是更加容易预测。为了两全其美(性能和可预测性)可以在同一时间调用两个系统并等着谁先完成。通常这会是第一个系统,但是在进度变得缓慢时,第二个系统就可以在可接受的时间内完成:
public CompletableFuture applyToEither(CompletionStage extends T> other,
Function super T, U> fn)
public static CompletableFuture allOf(CompletableFuture>... cfs)
public static CompletableFuture
allOf()当所有的潜在futures完成时,使用了一个futures数组并且返回一个future(等待所有的障碍)。另一方面 anyOf()将会等待最快的潜在futures。