参考文章
带你了解了解Future和CompletableFuture
线程
线程请参考 线程实现方法
缺点:无法获取运行结果。
Future + Callable
Java 1.5开始,提供了Callable和Future,通过它们可以在线程任务执行完毕之后得到任务执行结果。
- boolean isDone();// 判断是否已经完成 。
- get() ; // 阻塞主线程,直到子线程运行结束,返回运行结果 。
- get(long timeout, TimeUnit unit) ; // 阻塞主线程,直到子线程运行结束,返回运行结果;或者时间超时 timeout,报错 。
public static void test_1() {
try {
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> result = executor.submit(() -> {
System.out.println("线程开始");
Thread.sleep(30000);
System.out.println("线程结束");
return new Random().nextInt();
});
System.out.println("主线程-1");
Thread.sleep(10000);
System.out.println("主线程-2");
//shutdown调用后,不可以再submit新的task,已经submit的将继续执行。
executor.shutdown();
Thread.sleep(10000);
System.out.println("主线程-3");
System.out.println("result:" + result.get());
System.out.println("主线程-4");
} catch (Exception e) {
e.printStackTrace();
}
}
运行结果:
主线程-1
线程开始
主线程-2
主线程-3
线程结束
result:-1240584785
主线程-4
从结果可以看到:
- ExecutorService.submit 提交后新建子线程就立马执行
- ExecutorService.shutdown 调用后,不可以再submit新的task,否则报错。已经submit的将继续执行。
- Future.get() 阻塞主线程,等子线程获取结果后才可以继续主线程
缺点:Future很难直接表述多个Future 结果之间的依赖性,如将两个异步计算合并为一个等。
CompletableFuture
CompletableFuture类实现了CompletionStage和Future接口,因此你可以像Future那样使用它的 isDone(),get(),get(long timeout, TimeUnit unit) 等方法。
CompletableFuture保留了Future的优点,并且弥补了其不足。即异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。
CompletableFuture的用法
-
创建异步操作,runAsync(不支持返回值) 和 supplyAsync方法(支持返回值)
-
whenComplete ,handle ,thenApply ,thenAccept 功能类似,都是 执行完当前任务的线程后,继续执行 下一布 的任务。
// whenComplete,handle 接受上一步的结果,和异常。 // whenComplete 没有返回值,不影响join的结果,handle 有返回值,影响join的结果 CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) // thenAccept,thenApply 只接受上一步的结果,如果有异常则不会进入,可以和exceptionally搭配捕获异常 // thenAccept 没有返回值,不影响join的结果,thenApply有返回值,影响join的结果 CompletableFuture<Void> thenAccept(Consumer<? super T> action) <U> CompletableFuture<U> thenApply( Function<? super T,? extends U> fn)
-
exceptionally:当前任务出现异常时,执行exceptionally中的回调方法。
-
thenRun 方法,执行完当前任务的线程后,继续执行 下一布 的任务,不关心任务的处理结果。
-
thenCombine 合并任务,thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。
-
thenCompose 方法,thenCompose 方法允许你对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
Async方法都是异步方法
Async结尾的方法都是可以异步执行的。如果指定了线程池,会在指定的线程池中执行,如果没有指定,默认会ForkJoinPool.commonPool()中执行。
runAsync方法:它以Runnabel函数式接口类型为参数,所以CompletableFuture的计算结果为空。
public static CompletableFuture<Void> runAsync(Runnable runnable)
// executor 线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
supplyAsync方法以Supplier函数式接口类型为参数,CompletableFuture的计算结果类型为U。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// executor 线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
thenApply 变换结果
这些方法的输入是上一个阶段计算后的结果,返回值是经过转化后结果
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor);
实例:
private static void test_3() {
System.out.println("主线程 1");
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("CompletableFuture step1 - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("CompletableFuture step1 - end ");
return "CompletableFuture step1";
}).thenApplyAsync(v -> {
System.out.println("CompletableFuture step2 - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("CompletableFuture step2 - end ");
return v + " -> CompletableFuture step2";
});
System.out.println("主线程 2");
System.out.println(future.join());
System.out.println("主线程 3");
}
运行结果:
主线程 1
CompletableFuture step1 - start
主线程 2
CompletableFuture step1 - end
CompletableFuture step2 - start
CompletableFuture step2 - end
CompletableFuture step1 -> CompletableFuture step2
主线程 3
结果分析:
- supplyAsync,thenApplyAsync 都是在子线程运行的不阻塞主线程。
- thenApplyAsync 接受上一个阶段计算后的结果,返回值是经过转化后结果
- CompletableFuture.join() 不需要捕捉异常。备注:CompletableFuture.get() try-catch 包裹,捕捉异常。
- CompletableFuture.join 阻塞主线程运行
备注:其实上面的代码,本身就是同步的代码,所以可以没必要写thenApply 。
thenAccept 消费结果
这些方法的输入是上一个阶段计算后的结果,消费这些结果,没有返回值
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
实例:
private static void test_4() {
System.out.println("主线程 1");
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("CompletableFuture step1 - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("CompletableFuture step1 - end ");
return "CompletableFuture step1";
}).thenAccept(v -> {
System.out.println("CompletableFuture step2 - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("CompletableFuture step2 - end ");
});
System.out.println("主线程 2");
System.out.println(future.join());
System.out.println("主线程 3");
}
运行结果:
主线程 1
CompletableFuture step1 - start
主线程 2
CompletableFuture step1 - end
CompletableFuture step2 - start
CompletableFuture step2 - end
null
主线程 3
结果分析: thenAccept 和 thenApply 功能类似,只是 thenAccept 没有返回值, thenApply 有返回值
thenCombine 混合两个CompletionStage(2个线程)的结果
henCombine 混合两个CompletionStage(2个线程)的结果,并进行转化后返回
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor)
样例:
private static void test_5() {
System.out.println("主线程 1");
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("CompletableFuture step1 - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("CompletableFuture step1 - end ");
return "CompletableFuture step1";
}).thenCombineAsync(CompletableFuture.supplyAsync(() -> {
System.out.println("CompletableFuture step2 - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("CompletableFuture step2- end ");
return "CompletableFuture step2";
}), (s1, s2) -> {
return s1 + " " + s2;
});
System.out.println("主线程 2");
System.out.println(future.join());
System.out.println("主线程 3");
}
运行结果:
主线程 1
CompletableFuture step1 - start
CompletableFuture step2 - start
主线程 2
CompletableFuture step1 - end
CompletableFuture step2- end
CompletableFuture step1 CompletableFuture step2
主线程 3
结果分析:
- CompletableFuture step1 和 CompletableFuture step1 都是在子线程同时运行的,可见thenCombine 支持异步。
- thenCombineAsync 支持两个异步计算合并为一个,可以解决Future的问题
thenAcceptBoth 和 thenCombine 功能类似,都可以合并个异步计算结果,只是 thenCombine 有返回值,thenAcceptBoth 没有返回值
exceptionally 捕获异常
private static void test_6() {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("a - start ");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("a - end ");
return 100 / 0;
}).exceptionally(e -> {
System.out.println("a - exception ");
e.printStackTrace();
return 0;
});
System.out.println("result=" + future.join());
}
运行结果:
a - start
a - end
a - exception
result=0
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero......