Java CompletableFuture 详解
java 8 异步执行方法

java 8 异步执行方法Java 8 异步执行方法在Java 8之前,执行异步任务需要使用线程池或者手动创建线程来实现。
然而,Java 8引入了CompletableFuture类,使得异步任务的执行更加简单和高效。
CompletableFuture是一个可以包含异步操作结果的容器,它可以在完成时触发一些回调函数或者组合多个CompletableFuture实例。
在本文中,我们将学习如何使用Java 8的CompletableFuture类来实现异步执行方法。
1. 创建CompletableFuture实例CompletableFuture类提供了一些静态方法来创建CompletableFuture实例。
例如,我们可以使用supplyAsync()方法来创建一个CompletableFuture实例,并指定异步任务的执行逻辑。
下面是一个示例代码:```javaCompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 异步任务的执行逻辑return "Hello, World!";});在上述代码中,我们使用lambda表达式来定义异步任务的执行逻辑。
supplyAsync()方法返回一个CompletableFuture实例,该实例可以在异步任务执行完成后获取结果。
2. 添加回调函数CompletableFuture类提供了一些方法来添加回调函数,以便在异步任务执行完成后触发相应的操作。
例如,我们可以使用thenApply()方法来添加一个回调函数,该函数将在异步任务执行完成后对结果进行处理。
下面是一个示例代码:```javaCompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 异步任务的执行逻辑return "Hello, World!";});CompletableFuture<Integer> newFuture = future.thenApply(result -> {// 对结果进行处理的回调函数return result.length();});在上述代码中,我们使用thenApply()方法添加了一个回调函数,该函数将在异步任务执行完成后对结果进行处理。
java异步线程实现方式

java异步线程实现方式Java异步线程实现方式是Java编程语言中最核心和重要的技术之一。
异步线程可以用来进行并发操作,提高程序的执行效率和响应速度。
Java语言提供了多种实现异步线程的方式,包括线程池、Future、CompletableFuture等,下面我将详细介绍这些实现方式。
一、线程池线程池是Java中实现异步线程的最基础和常用的方法之一。
线程池有一定数量的线程,可以重复利用这些线程,避免创建和销毁线程的时间开销。
线程池通常有两个基本接口:Executor和ExecutorService。
Executor接口用来执行任务,ExecutorService接口用来管理和控制线程池。
线程池的使用方式:1.创建线程池对象2.提交任务给线程池执行3.关闭线程池二、FutureJava中的Future可以用来获取异步线程执行结果。
Future是一个接口,可以通过它的get()方法异步获取计算的结果。
Future支持取消异步任务,可以使用isDone()方法检查异步任务是否完成。
Future的使用方式:1.向ExecutorService提交Callable任务2.通过Future获取异步任务的结果3.关闭ExecutorService三、CompletableFutureJava8提供的CompletableFuture是一种新的实现异步线程的方式。
CompletableFuture通过链式调用实现了类似于JavaScript中的Promise的功能。
通过thenApply()、thenAccept()、thenRun()等方法可以实现回调,从而更加优雅地实现异步线程。
CompletableFuture的使用方式:1.创建CompletableFuture对象2.向CompletableFuture提交任务3.注册回调函数4.获取异步任务的结果以上是Java中最常用的三种实现异步线程的方式。
针对不同的场景,可以使用不同的实现方式。
Java8新的异步编程方式CompletableFuture实现

Java8新的异步编程⽅式CompletableFuture实现⼀. FutureJDK 5引⼊了Future模式。
Future接⼝是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进⾏异步计算。
Future模式是多线程设计常⽤的⼀种设计模式。
Future模式可以理解成:我有⼀个任务,提交给了Future,Future替我完成这个任务。
期间我⾃⼰可以去做任何想做的事情。
⼀段时间之后,我就便可以从Future那⼉取出结果。
Future的接⼝很简单,只有五个⽅法。
public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;}Future接⼝的⽅法介绍如下:boolean cancel (boolean mayInterruptIfRunning) 取消任务的执⾏。
参数指定是否⽴即中断任务执⾏,或者等等任务结束boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 trueboolean isDone () 任务是否已经完成。
需要注意的是如果任务正常终⽌、异常或取消,都将返回trueV get () throws InterruptedException, ExecutionException 等待任务执⾏结束,然后获得V类型的结果。
InterruptedException 线程被中断异常, ExecutionException任务执⾏异常,如果任务被取消,还会抛出CancellationExceptionV get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上⾯的get功能⼀样,多了设置超时时间。
java异步原理

java异步原理
Java中的异步原理是通过多线程实现的。
在传统的同步编程中,代码是按照顺序执行的,遇到阻塞操作时会发生阻塞,直到操作完成才会继续执行下一行代码。
而异步编程则可以在进行阻塞操作时,不必等待结果返回,而是可以继续执行后续的代码逻辑。
Java中异步编程常用的方式有使用线程池和Future,以及Java 8以后引入的CompletableFuture。
使用线程池和Future可以通
过submit方法提交任务到线程池中,返回一个Future对象,
通过该对象可以在需要的时候获取异步任务的结果。
当需要获取异步任务的结果时,可以通过调用Future对象的get方法来
阻塞等待结果返回。
CompletableFuture是Java 8引入的新特性,用于简化异步编程的复杂性。
它可以通过一系列的操作链来实现异步任务之间的依赖关系,如thenApply、thenAccept、thenCompose等方法。
通过这些方法可以实现任务的串行执行,也可以实现任务的并行执行。
CompletableFuture还提供了各种回调方法和异常处理方法,可以方便地处理异步任务的结果。
在异步编程中,需要注意处理线程安全问题。
多线程同时访问共享的资源时,可能会导致数据不一致或者线程安全问题。
可以通过使用锁机制或者使用线程安全的数据结构来解决这些问题。
总之,Java中的异步编程通过多线程实现,可以通过线程池和
Future、CompletableFuture等方式来实现异步任务的执行和结果的处理。
同时需要注意处理线程安全问题。
CompletableFuture基本用法

CompletableFuture基本⽤法异步计算所谓异步调⽤其实就是实现⼀个可⽆需等待被调⽤函数的返回值⽽让操作继续运⾏的⽅法。
在 Java 语⾔中,简单的讲就是另启⼀个线程来完成调⽤中的部分计算,使调⽤继续运⾏或返回,⽽不需要等待计算结果。
但调⽤者仍需要取线程的计算结果。
JDK5新增了Future接⼝,⽤于描述⼀个异步计算的结果。
虽然 Future 以及相关使⽤⽅法提供了异步执⾏任务的能⼒,但是对于结果的获取却是很不⽅便,只能通过阻塞或者轮询的⽅式得到任务的结果。
阻塞的⽅式显然和我们的异步编程的初衷相违背,轮询的⽅式⼜会耗费⽆谓的 CPU 资源,⽽且也不能及时地得到计算结果。
以前我们获取⼀个异步任务的结果可能是这样写的Future 接⼝的局限性Future接⼝可以构建异步应⽤,但依然有其局限性。
它很难直接表述多个Future 结果之间的依赖性。
实际开发中,我们经常需要达成以下⽬的:1. 将多个异步计算的结果合并成⼀个2. 等待Future集合中的所有任务都完成3. Future完成事件(即,任务完成以后触发执⾏动作)4. 。
函数式编程CompletionStageCompletionStage代表异步计算过程中的某⼀个阶段,⼀个阶段完成以后可能会触发另外⼀个阶段⼀个阶段的计算执⾏可以是⼀个Function,Consumer或者Runnable。
⽐如:stage.thenApply(x -> square(x)).thenAccept(x ->System.out.print(x)).thenRun(() -> System.out.println())⼀个阶段的执⾏可能是被单个阶段的完成触发,也可能是由多个阶段⼀起触发CompletableFuture在Java8中,CompletableFuture提供了⾮常强⼤的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能⼒,可以通过回调的⽅式处理计算结果,也提供了转换和组合 CompletableFuture 的⽅法。
Java8CompletableFuture异步执行操作

Java8CompletableFuture异步执⾏操作⽬录1.简介2.异步执⾏3.守护线程4.处理执⾏结果1.简介CompletableFuture 是 JDK8 提供的⼀个异步执⾏⼯具。
⽰例1:public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {for (int i = 0; i < 3; i++) {System.out.println(i);try {Thread.sleep(1000L);} catch (InterruptedException ignored) {}}System.out.println("Future Finished.");});System.out.println("Main Thread Finished.");future.get();}输出结果1:2.异步执⾏CompletableFuture 提供了两个⽅法⽤于异步执⾏:CompletableFuture.runAsync,没有返回值;CompletableFuture.supplyAsync,有返回值。
⽰例:public static void main(String[] args) throws ExecutionException, InterruptedException {// runAsync 没有返回值CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> System.out.println("future1 executed."));// supplyAsync 有返回值CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {System.out.println("future2 executed.");return "result";});System.out.println("future1.get(): " + future1.get());System.out.println("future2.get(): " + future2.get());}输出结果:3.守护线程CompletableFuture返回的Future默认为守护线程,如果不调⽤get()获取结果,主线程结束后会⾃动结束。
java异步编排的实现方式
java异步编排的实现方式Java异步编排是一种在多个任务之间进行协调和管理的技术,可以提高程序的执行效率和性能。
在Java中,有多种实现方式可以实现异步编排,例如使用线程池、Future和CompletableFuture等。
一、线程池线程池是一种用于管理和复用线程的机制。
在Java中,可以通过ThreadPoolExecutor类来创建和使用线程池。
线程池可以通过提供一定数量的线程来执行任务,当任务执行完毕后,线程可以被复用,避免了线程的频繁创建和销毁。
使用线程池可以实现异步编排的效果,通过提交任务到线程池中,线程池会按照一定的调度算法来执行任务,并提供一些监控和控制的机制。
二、FutureFuture是Java提供的一个接口,用于表示一个异步计算的结果。
通过Future可以获取异步计算的结果,或者取消异步计算的执行。
在Java中,可以通过ExecutorService.submit方法返回一个Future对象,然后通过Future的get方法来获取异步计算的结果。
使用Future可以实现异步编排,通过提交多个任务到线程池中,然后通过Future来获取任务的执行结果,当所有任务都执行完毕后,再进行下一步的操作。
三、CompletableFutureCompletableFuture是Java 8中引入的一个新特性,用于简化异步编程的复杂性。
CompletableFuture可以将多个异步任务串行或并行地执行,并提供了丰富的方法来处理异步任务的结果。
使用CompletableFuture可以实现更加灵活和高效的异步编排。
例如,可以使用CompletableFuture的thenCompose方法将多个任务串行执行,或者使用CompletableFuture的allOf方法来等待所有任务执行完毕后再进行下一步的操作。
总结:Java异步编排是一种提高程序执行效率和性能的技术。
在Java中,可以使用线程池、Future和CompletableFuture等方式来实现异步编排。
Java8的异步利器CompletableFuture源码解析(建议精读)
Java8的异步利器CompletableFuture源码解析(建议精读)(1)Function(2)Consumer对于前面有Bi的就是这样的,BiConsumer就是两个参数的。
(3)Predicate这个接口声明是一个入参,返回一个boolean。
(4)supplier这些任务中带有supply是持有返回值的,run是void返回值的,在玩supply时发现一个问题:如果使用supplyAsync任务时不使用任务的返回值,即不用get方法阻塞主线程会导致任务执行中断。
注:跟get方法无关,后面有答案然后我开始探索是否是只有 supplyAsync 是这样。
我测试了runAsync 发现也是这样。
下图为与 supplyAsync 任务执行不全面一样的问题,我甚至测试了将lambda换成runnable发现无济于事。
(4)取值方法,除了get还有一个 getNow(; 这个就比较特殊了。
这个方法是执行这个方法的时候任务执行完了就返回任务的结果,如果任务没有执行完就返回你的入参。
(5)join方法跟线程的join用法差不多。
下面是任务执行的线程的探索。
(7) then方法瞅着挺多的,实际上就是异不异步和加不加自定义Executor综上:这个线程的问题并不是大问题,只要你不用线程来做判断条件,他并不会影响你的效率。
试想pool线程都执行完了就用主线程跑呗。
没跑完,而使你等了那你就用pool线程呗。
thenRun就是这个任务运行完,再运行下一个任务,感觉像是join了一下。
其余不再介绍,大同小异。
像 thenApply(Function); 这样的就是有入参有返回值类型的。
像 thenAccept(Consumer); 这样的就是有入参,但是没有返回值的。
详情在上文中有过关于函数式接口的叙述。
completefuture详解
completefuture详解
CompletableFuture是Java8提供的一种基于Future的异步编程的实现,它是为了解决Future的缺点而提出的,它不仅可以代表异步
计算的结果,还能够定义它完成之后的回调函数,它的实现在
pletableFuture的包内。
CompletableFuture的实现涉及到JDK8的几个新特性:Lambda、Stream、线程池等,它允许把任务池、线程池和阻塞队列组合在一起,使得开发者可以更加简单地实现异步编程,对程序的执行性能也带来
了一定的提升。
CompletableFuture可以把一个异步任务串行化或者并行化,这
样就不再需要使用Future的那种臃肿的结构了,CompletableFuture
可以通过各种方法,来实现复杂的业务异步化。
它还提供了CompletionStage机制,使得开发者可以在多个CompletableFuture结果完成后,进行特定的合并计算操作。
CompletableFuture还提供了流式调用API,可以用来组织多个异步操作之间的依赖关系,从而实现一个复杂的逻辑。
Java8 异步编程利器 CompletableFuture 详解(全网看这一篇就行)
一、简介1.1 概述我们知道Future的有局限性,它没法直接对多个任务进行链式、组合等处理,需要借助并发工具类才能完成,实现逻辑比较复杂。
从其中一种程度上说,这项能力是它的核心能力。
而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。
1.2 功能1.2.1 常用方法依赖关系•thenApply(:把前面任务的执行结果,交给后面的Functionand集合关系•thenAccepetBoth(:两个任务执行完成后,将结果交给thenAccepetBoth处理,无返回值•runAfterBoth(:两个任务都执行完成后,执行下一步操作(Runnable类型任务)or聚合关系•applyToEither(:两个任务哪个执行的快,就使用哪一个结果,有返回值•acceptEither(:两个任务哪个执行的快,就消费哪一个结果,无返回值•runAfterEither(:任意一个任务执行完成,进行下一步操作(Runnable类型任务)并行执行结果处理1.2.2 异步操作这四个方法的区别:•runAsync( 以Runnable函数式接口类型为参数,没有返回结果,supplyAsync( 以Supplier函数式接口类型为参数,返回结果类型为U;Supplier接口的 get(是有返回值的(会阻塞)异步操作获取结果(join&get)结果处理•Action的类型是BiConsumer<? super T,? super Throwable>,它可以处理正常的计算结果,或者异常情况。
•方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
上面的代码当出现异常时,输出结果如下执行失败:ng.ArithmeticException: / by zero null 执行完成!二、应用场景2.1 结果转换将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Future是Java 5添加的类,用来描述一个异步计算的结果。
你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel 方法停止任务的执行。
虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。
阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?很多语言,比如Node.js,采用回调的方式实现异步编程。
Java的一些框架,比如Netty,自己扩展了Java的Future接口,提供了addListener等多个扩展方法:Google guava也提供了通用的扩展Future:ListenableFuture、SettableFuture以及辅助类Futures 等,方便异步编程。
Scala也提供了简单易用且功能强大的Future/Promise异步编程模式。
作为正统的Java类库,是不是应该做点什么,加强一下自身库的功能呢?在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。
下面我们就看一看它的功能吧。
主动完成计算CompletableFuture类实现了CompletionStage和Future接口,所以你还是可以像以前一样通过阻塞或者轮询的方式获得结果,尽管这种方式不推荐使用。
getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。
join返回计算的结果或者抛出一个unchecked异常(CompletionException),它和get对抛出的异常的处理有些细微的区别,你可以运行下面的代码进行比较:尽管Future可以代表在另外的线程中执行的一段异步代码,但是你还是可以在本身线程中执行:上面的代码中future没有关联任何的Callback、线程池、异步任务等,如果客户端调用future.get就会一致傻等下去。
你可以通过下面的代码完成一个计算,触发客户端的等待:当然你也可以抛出一个异常,而不是一个成功的计算结果:完整的代码如下:可以看到我们并没有把plete(100);放在另外的线程中去执行,但是在大部分情况下我们可能会用一个线程池去执行这些异步任务。
plete()、pleteExceptionally只能被调用一次。
但是我们有两个后门方法可以重设这个值:obtrudeValue、obtrudeException,但是使用的时候要小心,因为complete 已经触发了客户端,有可能导致客户端会得到不期望的结果。
创建CompletableFuture对象。
pletedFuture是一个静态辅助方法,用来返回一个已经计算好的CompletableFuture。
而以下四个静态方法用来为一段异步执行的代码创建CompletableFuture对象:以Async结尾并且没有指定Executor的方法会使用monPool()作为它的线程池执行异步代码。
runAsync方法也好理解,它以Runnable函数式接口类型为参数,所以CompletableFuture 的计算结果为空。
supplyAsync方法以Supplier<U>函数式接口类型为参数,CompletableFuture的计算结果类型为U。
因为方法的参数类型都是函数式接口,所以可以使用lambda表达式实现异步任务,比如:计算结果完成时的处理当CompletableFuture的计算结果完成,或者抛出异常的时候,我们可以执行特定的Action。
主要是下面的方法:可以看到Action的类型是BiConsumer<? super T,? super Throwable>,它可以处理正常的计算结果,或者异常情况。
方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
注意这几个方法都会返回CompletableFuture,当Action执行完毕后它的结果返回原始的CompletableFuture的计算结果或者返回异常。
exceptionally方法返回一个新的CompletableFuture,当原始的CompletableFuture抛出异常的时候,就会触发这个CompletableFuture的计算,调用function计算值,否则如果原始的CompletableFuture正常计算完后,这个新的CompletableFuture也计算完成,它的值和原始的CompletableFuture的计算的值相同。
也就是这个exceptionally方法用来处理异常的情况。
下面一组方法虽然也返回CompletableFuture对象,但是对象的值和原来的CompletableFuture 计算的值不同。
当原先的CompletableFuture的值计算完成或者抛出异常的时候,会触发这个CompletableFuture对象的计算,结果由BiFunction参数计算而得。
因此这组方法兼有whenComplete和转换的两个功能。
同样,不以Async结尾的方法由原来的线程计算,以Async结尾的方法由默认的线程池monPool()或者指定的线程池executor运行。
转换CompletableFuture可以作为monad(单子)和functor。
由于回调风格的实现,我们不必因为等待一个计算完成而阻塞着调用线程,而是告诉CompletableFuture当计算完成的时候请执行某个function。
而且我们还可以将这些操作串联起来,或者将CompletableFuture组合起来。
这一组函数的功能是当原来的CompletableFuture计算完后,将结果传递给函数fn,将fn的结果作为新的CompletableFuture计算结果。
因此它的功能相当于将CompletableFuture<T>转换成CompletableFuture<U>。
这三个函数的区别和上面介绍的一样,不以Async结尾的方法由原来的线程计算,以Async 结尾的方法由默认的线程池monPool()或者指定的线程池executor运行。
Java的CompletableFuture类总是遵循这样的原则,下面就不一一赘述了。
使用例子如下:需要注意的是,这些转换并不是马上执行的,也不会阻塞,而是在前一个stage完成后继续执行。
它们与handle方法的区别在于handle方法会处理正常计算值和异常,因此它可以屏蔽异常,避免异常继续抛出。
而thenApply方法只是用来处理正常值,因此一旦有异常就会抛出。
纯消费(执行Action)上面的方法是当计算完成的时候,会生成新的计算结果(thenApply, handle),或者返回同样的计算结果whenComplete,CompletableFuture还提供了一种处理结果的方法,只对结果执行Action,而不返回新的计算值,因此计算值为Void:看它的参数类型也就明白了,它们是函数式接口Consumer,这个接口只有输入,没有返回值。
thenAcceptBoth以及相关方法提供了类似的功能,当两个CompletionStage都正常完成计算的时候,就会执行提供的action,它用来组合另外一个异步的结果。
runAfterBoth是当两个CompletionStage都正常完成计算的时候,执行一个Runnable,这个Runnable并不使用计算的结果。
例子如下:更彻底地,下面一组方法当计算完成的时候会执行一个Runnable,与thenAccept不同,Runnable并不使用CompletableFuture计算的结果。
因此先前的CompletableFuture计算的结果被忽略了,这个方法返回CompletableFuture<Void>类型的对象。
因此,你可以根据方法的参数的类型来加速你的记忆。
Runnable类型的参数会忽略计算的结果,Consumer是纯消费计算结果,BiConsumer会组合另外一个CompletionStage纯消费,Function 会对计算结果做转换,BiFunction会组合另外一个CompletionStage的计算结果做转换。
组合这一组方法接受一个Function作为参数,这个Function的输入是当前的CompletableFuture 的计算值,返回结果将是一个新的CompletableFuture,这个新的CompletableFuture会组合原来的CompletableFuture和函数返回的CompletableFuture。
因此它的功能类似:记住,thenCompose返回的对象并不一是函数fn返回的对象,如果原来的CompletableFuture还没有计算出来,它就会生成一个新的组合后的CompletableFuture。
例子:而下面的一组方法thenCombine用来复合另外一个CompletionStage的结果。
它的功能类似:两个CompletionStage是并行执行的,它们之间并没有先后依赖顺序,other并不会等待先前的CompletableFuture执行完毕后再执行。
其实从功能上来讲,它们的功能更类似thenAcceptBoth,只不过thenAcceptBoth是纯消费,它的函数参数没有返回值,而thenCombine的函数参数fn有返回值。
EitherthenAcceptBoth和runAfterBoth是当两个CompletableFuture都计算完成,而我们下面要了解的方法是当任意一个CompletableFuture计算完成的时候就会执行。
acceptEither方法是当任意一个CompletionStage完成的时候,action这个消费者就会被执行。
这个方法返回CompletableFuture<Void>applyToEither方法是当任意一个CompletionStage完成的时候,fn会被执行,它的返回值会当作新的CompletableFuture<U>的计算结果。
下面这个例子有时会输出100,有时候会输出200,哪个Future先完成就会根据它的结果计算。