JDK8探险——CompletableFuture
来源:互联网 发布:淘宝把买家加入黑名单 编辑:程序博客网 时间:2024/05/16 19:32
Java的线程不只是Thread,调度不止是.start、.join、.sleep。
jdk5引入Future、Callable,多线程调度有了很多高级应用。JDK8开始之后引入lambda,程序调度流程粒度进一步缩小,配合新的线程相关API,出现了很多看起来优雅,功能强大,使用要求高的线程调度用法。
CompletableFuture 方法探析。
1. supplyAsync
相关方法
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fnpublic <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
CompletableFuture的静态方法,接受一个Function生成一个新的CompletableFuture对象,对象生成时异步执行传入的Function;< U >泛型为接受的Function的返回值类型。
查看源码如下:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return asyncSupplyStage(asyncPool, supplier);}
指定默认的线程池asyncPool调用了asyncSupplyStage方法返回CompletableFuture,asyncPool是CompletableFuture私有静态属性。supplyAsync有一个重载方法,可以指定执行的连接池。【supplyAsync(Supplier< U > supplier, Executor executor)】
关于asyncSupplyStage方法如下:
static <U> CompletableFuture<U> asyncSupplyStage(Executor e,Supplier<U> f){ if (f == null) throw new NullPointerException(); CompletableFuture<U> d = new CompletableFuture<U>(); e.execute(new AsyncSupply<U>(d, f)); return d;}
初始化一个CompletableFuture< U >对象,和传入的Function一同包装成一个AsyncSupply对象,根据继承关系,AsyncSupply是Runnable接口的实现类。在AsyncSupply重写run方法,作用就是将Fanction结果的返回值设置到CompletableFuture对象中,完成后继续下一个任务。
public void run() { CompletableFuture<T> d; Supplier<T> f; if ((d = dep) != null && (f = fn) != null) { dep = null; fn = null; if (d.result == null) { try { d.completeValue(f.get()); } catch (Throwable ex) { d.completeThrowable(ex); } } d.postComplete(); } }
综上所知:asyncSupplyStage方法中e.execute执行时会初始化一个Thread执行操作,传入的Function是异步执行不阻塞主线程。测试代码如下:
@Testpublic void supplyAsync() { CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("1 Say hello!" + Thread.currentThread()); return "hello"; }); CompletableFuture.supplyAsync(() -> { System.out.println("2 Say hello!" + Thread.currentThread()); return "hello"; }); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }}
运行结果:.
2 Say hello!Thread[ForkJoinPool.commonPool-worker-2,5,main]1 Say hello!Thread[ForkJoinPool.commonPool-worker-1,5,main]
2.thenApplyAsync,变换
相关方法
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);
使用示例:
@Testpublic void thenApply() throws InterruptedException, ExecutionException { CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> "12"); CompletableFuture<Integer> thenApply = supplyAsync.thenApplyAsync(s -> { System.out.println("String:" + s); return Integer.valueOf(s); }); Integer result = thenApply.get(); System.out.println("int:" + result);}
方法的入参fn是一个函数式接口,这个fn的入参是上一步的计算结果“12”,get()会阻塞主线程等待执行结果返回,最终打印结果:
String:12int:12
3.thenAcceptAsync,消耗
相关方法
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);
从方法名含义上理解,接受上一个执行结果,应用一个没有返回值(Void)的Consumer。
测试用例:
@Testpublic void thenAccept() { CompletableFuture.supplyAsync(() -> "hello").thenAcceptAsync(s -> { System.out.println(s + " world"); });}
虽然入参的Consumer对象是不期待返回值的,但是thenAcceptAsync方法是有返回值的。
4.thenRunAsync
相关方法
public CompletionStage<Void> thenRun(Runnable action);public CompletionStage<Void> thenRunAsync(Runnable action);public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
在上一步执行结束之后,启动的一个线程,入参是一个Runnable类型的,不需要入参和返回值。和thenAccept相比,不需要之前执行结果的依赖。
测试方法
@Testpublic void thenRun() { CompletableFuture.supplyAsync(() -> { try { System.out.println(Thread.currentThread()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "hello"; }).thenRun(() -> { System.out.println("hello world in " + Thread.currentThread()); System.exit(1); }); while (true) { System.out.println("while"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } }}
输出结果:
Thread[ForkJoinPool.commonPool-worker-1,5,main]whilewhilewhilewhilewhilewhilewhilewhilewhilewhilehello world in Thread[ForkJoinPool.commonPool-worker-1,5,main]
从结果上看,Runnable 使用的线程池和supplyAsync应用的是同一个。
5.thenCombine,结合
相关方法:
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);
获取上一个的返回结果,结合other的返回结果,两个结果作为参数共同传入BiFunction中计算一个最终结果。
测试方法:
@Testpublic void thenCombine() { String result = CompletableFuture.supplyAsync(() -> { try { System.out.println("hello in " + Thread.currentThread()); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "hello"; }).thenCombine(CompletableFuture.supplyAsync(() -> { try { System.out.println("world in " + Thread.currentThread()); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return "world"; }), (s1, s2) -> { System.out.println("hello world in " + Thread.currentThread()); return s1 + " " + s2; }).join(); System.out.println("main get result:"+result);}
执行结果:
hello in Thread[ForkJoinPool.commonPool-worker-1,5,main]world in Thread[ForkJoinPool.commonPool-worker-2,5,main]hello world in Thread[ForkJoinPool.commonPool-worker-2,5,main]main get result:hello world
分任务在不同的线程里并行执行,第二个字任务和汇总任务因为都是thenCombine的入参,在同一个线程中进行。
6.thenAcceptBothAsync
相关方法:
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor);
thenCombine的无返回值版本。
7.runAfterBoth
相关方法:
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);
thenAcceptBothAsync的无入参版。不关注上一个和other的执行结果。
8.applyToEither,选一个快的
相关方法:
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);
测试方法:
@Testpublic void applyToEither() { String result = CompletableFuture.supplyAsync(() -> { int sleep = (int)(Math.random()*10); System.out.println(String.format("hello sleep %s s", sleep)); try { Thread.sleep(sleep*1000); } catch (InterruptedException e) { e.printStackTrace(); } return "hello"; }).applyToEither(CompletableFuture.supplyAsync(() -> { int sleep = (int)(Math.random()*10); System.out.println(String.format("hello world %s s", sleep)); try { Thread.sleep(sleep*1000); } catch (InterruptedException e) { e.printStackTrace(); } return "world"; }), s -> s).join(); System.out.println("main get result:"+result);}
整体逻辑是,最终方法需要一个入参去计算结果,获得这个参数有两种途径,哪个快就用哪个的计算结果作为参数计算最终结果,慢的被丢弃。
测试输出结果:
hello sleep 9 shello world 3 smain get result:world
9.acceptEither & runAfterEither
同样的,applyToEither的Void版acceptEither和无参Void版runAfterEither。
相关方法:
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action,Executor executor);public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);
10. exceptionally,异常补偿
相关方法
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
代码发生异常时,进行补偿,类似try-catch的功能。
测试方法:
@Testpublic void exceptionally() { String result = CompletableFuture.supplyAsync(() -> { try { System.out.println(Thread.currentThread()); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } if (1 == 1) { throw new RuntimeException("测试一下异常情况"); } return "s1"; }).exceptionally(e -> { System.out.println(Thread.currentThread()); System.out.println(e.getMessage()); return "hello world"; }).join(); System.out.println(result);}
输出结果:
Thread[ForkJoinPool.commonPool-worker-1,5,main]Thread[ForkJoinPool.commonPool-worker-1,5,main]java.lang.RuntimeException: 测试一下异常情况hello world
从打印线程信息看,异常处理部分的方法执行和发生异常的代码在同一个线程中执行。
11. whenComplete 结束记录。
相关方法:
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,Executor executor);
接收执行完成的结果,返回值或异常,不会影响最终执行结果。
与exceptionally进行比较,测试代码:
@Testpublic void whenComplete() { String result = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "s1"; }).whenComplete((s, t) -> { System.out.println(s); System.out.println(t); int i = 1/0; }).exceptionally(e -> { System.out.println(e.getMessage()); return "hello world"; }).join(); System.out.println(result);}
输出测试结果:
s1nulljava.lang.ArithmeticException: / by zerohello world
注意whenComplete中参数的空指针问题。
另一种情况:
@Testpublic void whenComplete() { String result = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }int i = 1/0; return "s1"; }).whenComplete((s, t) -> { System.out.println(s); System.out.println(t); }).exceptionally(e -> { System.out.println(e.getMessage()); return "hello world"; }).join(); System.out.println(result);}
执行结果:
nulljava.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zerojava.lang.ArithmeticException: / by zerohello world
可见,whenComplete只是将前一个的执行结果,无论是异常还是正常结果,传递到下面去。
12.handle,结果处理
相关方法:
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);
所谓结果,包含两种,一种是喜闻乐见的正常结果,另一种是“喜闻乐见”的异常结果。
发生异常测试:
@Testpublic void handle() { String result = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //出现异常 int i =1/0; return "s1"; }).handle((s, t) -> { if (t != null) { return "s2"; } return s; }).join(); System.out.println(result);}
执行结果:
s2
无异常测试:
@Testpublic void handle() { String result = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "s1"; }).handle((s, t) -> { if (t != null) { return "s2"; } return s; }).join(); System.out.println(result);}
结果:
s1
对比whenComplete,它功能更加强大,增加了可以影响最终结果的功能。
以上只是简单的单个或两个的结合方法的测试,在实际的业务中所需处理的逻辑只会更加复杂。CompletableFuture类的泛型和流式调用设计,在复杂业务的任务流程调用上有一定的便利。
参考:
简书:数齐—CompletableFuture 详解
- JDK8探险——CompletableFuture
- 数据结构探险—栈篇
- 用个小例子来介绍一下JDK8的CompletableFuture
- CompletableFuture
- CompletableFuture
- CompletableFuture
- CompletableFuture
- 数据结构探险——栈篇
- 数据结构探险——树篇
- 数据结构探险——图篇
- SpringMVC深度探险 —— SpringMVC概览
- SpringMVC深度探险 —— SpringMVC概览
- A*(伪——vijos丛林探险
- 数据结构探险——队列篇
- 数据结构探险——线性表篇
- 宝岛探险 深度优先—C
- JDK8 ——lambda表达式
- jdk8——Stream API
- Visual studio + IIS环境下跨域项目,移动端可触发断点调试的WEB服务器布署
- 第六章 ALDS1_5_A:Exhaustive Search 穷举搜索
- Springboot入门之分布式事务管理
- 474. Ones and Zeroes
- pip安装套件出错
- JDK8探险——CompletableFuture
- 数据结构笔记(3)树——AVL树以及恼人的旋转
- 哈尔滨理工大学第七届程序设计竞赛初赛(高年级组)I 旅行【枚举+spfa】
- SGMII 和 Serdes 的详细说明
- Java之android添加Button打印TextView
- 适配器模式
- python编程
- 746. Min Cost Climbing Stairs
- GDD 2016 (Google Developers Day)资源下载