一、什么是CompletableFuture
CompletableFuture
是java8提供的一个异步编程类,提供了一系列的异步编程方法。它同时实现了Future
和CompletionStage
接口。所以CompletableFuture
不但包含了Future
的功能特性,而且还实现了CompletionStage
接口定义的任务编排的方法,可以实现不同任务的运行顺序、规则以及方式。
二、方法API与实例
2.1 实例化CompletableFuture的方法
(1)构造方法实例化
CompletableFuture future = new CompletableFuture();
future.complete("zhangsan");
System.out.println(future.get());
通过构造方法创建的 CompletableFuture
实例对象没有定义任务,调用get()
方法是会阻塞拿不到返回值,可以使用 complete()
方法设置默认返回结果,主动触发任务完成。
(2)静态方法实例化
除了使用构造方法构造,CompletableFuture
还提供了静态方法来实例化
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
supplyAsync
:参数为Supplier
函数式接口类型,返回结果类型为U,调用get()
方法是有返回值的(会阻塞)
runAsync
:以Runnable
函数式接口类型为参数,没有返回结果,调用get()
方法会返回null(会阻塞)
Executor
参数就是用来执行异步任务的线程池,如果不传Executor
的话,默认是ForkJoinPool
这个线程池的实现。
示例:
public static void instanceTest() throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future1 = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
return "lisi";
}
},pool);
CompletableFuture future2 = CompletableFuture.supplyAsync(()->"lisi",pool);
CompletableFuture future3 = CompletableFuture.supplyAsync(()->{return "lisi";},pool);
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
}
输出:
lisi
lisi
lisi
future1、future2、future3三种写法效果是一样的,后两种是lamda的写法,比较简洁,建议使用。
public static void instanceTest1() throws ExecutionException, InterruptedException{
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.runAsync(()->{System.out.println("runAsync方法");},pool);
System.out.println(future.get());
}
输出:
runAsync方法
null
runAsync
方法构造的CompletableFuture
没有返回值,通过get
方法拿到的是null
使用静态方法的和使用构造方法主要区别就是,使用构造方法需要其它线程主动调用complete来表示任务执行完成。
2.2 获取任务执行结果方法
public T get()
public T get(long timeout, TimeUnit unit)
public T getNow(T valueIfAbsent)
public T join()
get()
:Future
接口提供的方法,会一直阻塞,直到任务结束,可以获取到结果(没有返回值的任务结果为null)。
get(long timeout, TimeUnit unit)
:Future
接口提供的方法,指定了超时时间,当到了指定的时间还未获取到任务,就会抛出TimeoutException异常。
getNow(T valueIfAbsent)
:立即获取任务的执行结果。不会产生阻塞,如果任务还没执行完成,那么就会返回你传入的 valueIfAbsent 参数值,如果执行完成了,就会返回任务执行的结果。
join()
:和get()
方法基本一致,区别在于·get()·会抛出检查时异常,·join()·不会。
2.3 主动触发任务完成方法
public boolean complete(T value)
public boolean completeExceptionally(Throwable ex)
complete
:主动触发当前异步任务的完成,如果任务已完成,返回false,如果任务没完成,就会返回true,通过get()/jion()获取的值就是complete
的参数值。
completeExceptionally
:主动触发当前异步任务的完成,如果任务已完成,返回false,如果任务没完成,就会返回true,通过get()/jion()获取的值就是completeExceptionally
设置的参数(也就是ex异常)。
complete示例:
CompletableFuture future = CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello!";
});
Thread.sleep(1000);
boolean flag = future.complete("你好!");
System.out.println("flag:"+flag + ",返回结果"+future.join());
返回结果
flag:true,返回结果你好!
completeExceptionally示例
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello!";
},pool);
Thread.sleep(1000);
boolean flag1 = future1.completeExceptionally(new Exception("主动触发异常"));
System.out.println("flag1:"+flag1);
System.out.println("返回结果"+future1.join());
返回结果
flag1:true
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.Exception: 主动触发异常
...具体异常信息省略...
2.4 任务执行完成后的下一步回调方法
(1)任务正常执行后的下一步回调方法
任务正常完成,没有出现异常的下一步回调方法
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
thenApply
:可以拿到上一步任务执行的结果进行处理,并且返回处理的结果
thenRun
:无需拿到上一步任务执行的结果的下一步处理方法,没有返回值
thenAccept
:可以拿到上一步任务执行的结果进行处理,没有返回值
示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()-> 5,pool)
.thenApply(v ->{
System.out.println("future第一步的处理结果:"+v);
return 10;
});
System.out.println("future的最终处理结果" + future.join());
CompletableFuture future1 = CompletableFuture.supplyAsync(()-> 5,pool)
.thenApply(v ->{
System.out.println("future1第一步的处理结果:"+v);
return 10;
})
.thenAccept(v->{
System.out.println("future1第二步的处理结果:"+v);
});
System.out.println("future1的最终处理结果" +future1.join());
CompletableFuture future2 = CompletableFuture.runAsync(()->{System.out.println("future2第一步处理");},pool)
.thenRun(()->{
System.out.println("future2第二步处理");
})
.thenAccept(v->{
System.out.println("future2第三步的处理结果:"+v);
});
返回结果:
future第一步的处理结果:5
future的最终处理结果10
future1第一步的处理结果:5
future1第二步的处理结果:10
future1的最终处理结果null
future2第一步处理
future2第二步处理
future2第三步的处理结果:null
下一步回调方法还有对应的异步方法:
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> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()-> {
System.out.println("threadName:"+Thread.currentThread().getName());
return 5;
},pool).thenApplyAsync(v ->{
System.out.println("threadName:"+Thread.currentThread().getName());
System.out.println("future第一步的处理结果:"+v);
return 10;
},pool);
System.out.println("future的最终处理结果" + future.join());
返回结果:
threadName:pool-1-thread-1
threadName:pool-1-thread-2
future第一步的处理结果:5
future的最终处理结果10
加了Async
的方法表示开启另外的线程执行回调方法。executor
参数表示使用定义的线程池。不传的话使用ForkJoinPool
默认线程池。
(2)任务执行出现异常的回调方法
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
当前面的任务执行出现异常后,会回调exceptionally
方法指定的回调,但是如果没有出现异常,是不会回调的。exceptionally
能够将异常给吞了,并且将fn的返回值会返回回去。
示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()-> {
int i = 10/0;
return 5;
},pool).thenApply(v ->{
System.out.println("future第一步的处理结果:"+v);
return 10;
}).exceptionally(e->{
System.out.println("捕获到异常:"+e.getMessage());
return 15;
});
System.out.println("future的最终处理结果" + future.join());
返回结果:
捕获到异常:java.lang.ArithmeticException: / by zero
future的最终处理结果15
(3)能同时接收任务执行正常和异常的回调方法
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> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
handle
:正常执行和出现异常都会回调,有返回值。会吞掉异常
whenComplete
:正常执行和出现异常都会回调,不会影响上一阶段的返回结果,无返回值。不会吞掉异常,也就是说主线程在获取执行异常任务的结果时,会抛出异常。
带Async
的方法和上面thenApplyAsync
类似。
示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()-> {
int i = 10/0;
return 5;
},pool).handle((r,e)->{
if(e!=null){
System.out.println("捕获到异常:"+e);
return 10;
}
System.out.println("future第一步的处理结果:"+r);
return 15;
});
System.out.println("future的最终处理结果" + future.join());
CompletableFuture future1 = CompletableFuture.supplyAsync(()-> {
int i = 10/0;
return 5;
},pool).whenComplete((r,e)->{
if(e!=null){
System.out.println("捕获到异常:"+e);
}
System.out.println("future1第一步的处理结果:"+r);
});
System.out.println("future1的最终处理结果" + future1.join());
返回结果:
捕获到异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
future的最终处理结果10
捕获到异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
future1第一步的处理结果:null
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
...异常信息省略
Caused by: java.lang.ArithmeticException: / by zero
...异常信息省略
可以看到whenComplete
没有吞掉异常,主线程调用join
方法时捕获到了异常
2.5 多个任务执行结果下一步回调方法
多个任务又由之间的关系分为一下几类
(1)依赖关系
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor)
thenCompose
:接收上一个任务的结果,给另一个任务处理。和thenApply
类似,对一个CompletableFuture
返回的结果进行后续操作,返回一个新的CompletableFuture
,区别在于thenApply
相当于将CompletableFuture<T>
转换生成新的CompletableFuture<U>
,而thenCompose
将两个CompletableFuture
合并生成新的CompletableFuture
。
带Async
的方法和上文类似,这里不再复述
示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()->{
System.out.println("test1_threadName:"+Thread.currentThread().getName());
return 5;
},pool).thenCompose(v ->{
return CompletableFuture.supplyAsync(()->{
System.out.println("test2_threadName:"+Thread.currentThread().getName());
System.out.println("第一个任务的返回结果:"+v);
return 10;
},pool);
});
System.out.println("future返回结果"+future.join());
返回结果:
test1_threadName:pool-1-thread-1
test2_threadName:pool-1-thread-2
第一个任务的返回结果:5
future返回结果10
从结果可以看出两个阶段的任务执行不是一个线程执行的。而thenApply
可能是同一个线程执行的(thenApply会出现由main线程即主线程执行,但是不会出现是线程池里的其他线程执行的情况)
(2)and关系
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor)
thenCombine
:合并处理两个任务的结果,返回一个新的结果。
thenAcceptBoth
:合并处理两个任务的结果,不返回结果
runAfterBoth
:两个任务都执行完成后,执行下一步操作(Runnable类型任务)
带Async的方法和上文类似,这里不再复述
示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture future = CompletableFuture.supplyAsync(()->{
System.out.println("test1_threadName:"+Thread.currentThread().getName());
return 5;
},pool).thenCombine(CompletableFuture.supplyAsync(()->{
System.out.println("test2_threadName:"+Thread.currentThread().getName());
return 10;
},pool),(t,u)->{
System.out.println("任务1结果:"+t);
System.out.println("任务2结果:"+u);
return 15;
});
System.out.println("合并后的future返回结果"+future.join());
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("test1_threadName:"+Thread.currentThread().getName());
return 5;
},pool).thenAcceptBoth(CompletableFuture.supplyAsync(()->{
System.out.println("test2_threadName:"+Thread.currentThread().getName());
return 10;
},pool),(t,u)->{
System.out.println("任务1结果:"+t);
System.out.println("任务2结果:"+u);
});
System.out.println("合并后的future1返回结果"+future1.join());
CompletableFuture future2 = CompletableFuture.runAsync(()->{
System.out.println("test1_threadName:"+Thread.currentThread().getName());
},pool).runAfterBoth(CompletableFuture.runAsync(()->{
System.out.println("test2_threadName:"+Thread.currentThread().getName());
},pool),()->{
System.out.println("任务1、任务2已完成,开始处理");
});
System.out.println("合并后的future2返回结果"+future2.join());
返回结果:
test1_threadName:pool-1-thread-1
test2_threadName:pool-1-thread-2
任务1结果:5
任务2结果:10
合并后的future返回结果15
test1_threadName:pool-1-thread-3
test2_threadName:pool-1-thread-1
任务1结果:5
任务2结果:10
合并后的future1返回结果null
test1_threadName:pool-1-thread-2
test2_threadName:pool-1-thread-2
任务1、任务2已完成,开始处理
合并后的future2返回结果null
(3) OR关系
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)
applyToEither
:两个任务谁执行的快,就使用谁的结果,有返回值
acceptEither
:两个任务谁执行的快,就使用谁的结果,无返回值
runAfterEither
:任意一个任务执行完成,进行下一步操作(Runnable类型任务)
示例:
ExecutorService pool = Executors.newFixedThreadPool(6);
CompletableFuture future = CompletableFuture.supplyAsync(()->{
System.out.println("future_task1_threadName:"+Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 5;
},pool).applyToEither(CompletableFuture.supplyAsync(()->{
System.out.println("future_task2_threadName:"+Thread.currentThread().getName());
return 10;
},pool),t ->{
System.out.println("已有任务执行完成,结果:"+t);
return 15;
});
System.out.println("合并后的future返回结果"+future.join());
CompletableFuture future1 = CompletableFuture.supplyAsync(()->{
System.out.println("future1_task1_threadName:"+Thread.currentThread().getName());
return 5;
},pool).acceptEither(CompletableFuture.supplyAsync(()->{
System.out.println("future1_task2_threadName:"+Thread.currentThread().getName());
return 10;
},pool),t ->{
System.out.println("已有任务执行完成,结果:"+t);
});
System.out.println("合并后的future1返回结果"+future1.join());
CompletableFuture future2 = CompletableFuture.supplyAsync(()->{
System.out.println("future2_task1_threadName:"+Thread.currentThread().getName());
return 5;
},pool).runAfterEither(CompletableFuture.supplyAsync(()->{
System.out.println("future2_task2_threadName:"+Thread.currentThread().getName());
return 10;
},pool),() ->{
System.out.println("已有任务执行完成");
});
System.out.println("合并后的future2返回结果"+future2.join());
返回结果:
future_task1_threadName:pool-1-thread-1
future_task2_threadName:pool-1-thread-2
已有任务执行完成,结果:10
合并后的future返回结果15
future1_task1_threadName:pool-1-thread-3
future1_task2_threadName:pool-1-thread-4
已有任务执行完成,结果:5
合并后的future1返回结果null
future2_task1_threadName:pool-1-thread-5
future2_task2_threadName:pool-1-thread-6
已有任务执行完成
合并后的future2返回结果null
(4)多个任务并行执行
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
allOf
:静态方法,当所有给定的 CompletableFuture 完成时,返回一个新的 CompletableFuture
anyOf
:静态方法,当任何一个给定的CompletablFuture完成时,返回一个新的CompletableFuture
allOf示例:
ExecutorService pool = Executors.newFixedThreadPool(4);
List<CompletableFuture> list = new ArrayList<>();
list.add(CompletableFuture.supplyAsync(()->{
System.out.println("future_task1_threadName:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task1执行完毕");
return 10;
},pool));
list.add(CompletableFuture.supplyAsync(()->{
System.out.println("future_task2_threadName:"+Thread.currentThread().getName());
return 5;
},pool));
CompletableFuture future = CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()]))
.handle((r,e)->{
System.out.println("全部任务执行完成,结果:"+r);
return 15;
});
System.out.println("最终的future返回结果"+future.join());
返回结果:
future_task1_threadName:pool-1-thread-1
future_task2_threadName:pool-1-thread-2
task1执行完毕
全部任务执行完成,结果:null
最终的future返回结果15
anyOf示例:
ExecutorService pool = Executors.newFixedThreadPool(4);
List<CompletableFuture> list = new ArrayList<>();
list.add(CompletableFuture.supplyAsync(()->{
System.out.println("future_task1_threadName:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task1执行完毕");
return 10;
},pool));
list.add(CompletableFuture.supplyAsync(()->{
System.out.println("future_task2_threadName:"+Thread.currentThread().getName());
return 5;
},pool));
CompletableFuture future = CompletableFuture.anyOf(list.toArray(new CompletableFuture[list.size()]))
.handle((r,e)->{
System.out.println("已经有任务完成,结果:"+r);
return 15;
});
System.out.println("最终的future返回结果"+future.join());
返回结果:
future_task1_threadName:pool-1-thread-1
future_task2_threadName:pool-1-thread-2
已经有任务完成,结果:5
最终的future返回结果15
task1执行完毕