文章目录
CompletableFuture
CompletableFuture
实现了CompletionStage
接口 和 Future
接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。
一、创建异步任务
1、ExecutorService.submit
ExecutorService
的submit
会返回Future
对象,通过Future
的get
方法获取返回值。
如果有异常,submit
会存下异常,只有当调用Future
的get
方法才会将任务执行时的异常重新抛出。
@Slf4j
public class ExecutorServiceSubmitTest {
private static final boolean errorFlag = false;
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(() -> {
String currentThreadName = Thread.currentThread().getName();
log.info(currentThreadName + ":start");
if (errorFlag) {
log.warn(currentThreadName + ":error");
throw new RuntimeException();
}
log.info(currentThreadName + ":finish");
return 666;
});
TimeUnit.SECONDS.sleep(2);
log.info("future result:");
// 等待子任务执行完成。
// 如果已完成则直接返回结果;如果执行任务异常,则get方法会把之前捕获的异常重新抛出
log.info("future result:" + future.get());
executorService.shutdown();
}
}
errorFlag为false的执行结果:2s后调用get获取结果
23:09:11.086 [pool-1-thread-1] INFO com.test.ExecutorServiceSubmitTest - pool-1-thread-1:start
23:09:11.087 [pool-1-thread-1] INFO com.test.ExecutorServiceSubmitTest - pool-1-thread-1:finish
23:09:13.088 [main] INFO com.test.ExecutorServiceSubmitTest - future result:
666
errorFlag为false的执行结果:2s后调用get才将异常抛出
23:09:54.075 [pool-1-thread-1] INFO com.test.ExecutorServiceSubmitTest - pool-1-thread-1:start
23:09:54.078 [pool-1-thread-1] WARN com.test.ExecutorServiceSubmitTest - pool-1-thread-1:error
23:09:56.079 [main] INFO com.test.ExecutorServiceSubmitTest - future result:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.test.ExecutorServiceSubmitTest.main(ExecutorServiceSubmitTest.java:35)
...
2、supplyAsync / runAsync
这两方法的效果跟submit
是一样的
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
// 创建「无返回值」的异步任务,相当于`ExecutorService submit(Runnable task)`方法
public static CompletableFuture<Void> runAsync(Runnable runnable) {...}
// 创建「有返回值」的异步任务,相当于`ExecutorService submit(Runnable task)`方法
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {...}
}
这两方法各有一个重载版本,可以指定执行异步任务的Executor实现
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
// 创建「无返回值」的异步任务,相当于`ExecutorService submit(Runnable task)`方法
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {...}
// 创建「有返回值」的异步任务,相当于`ExecutorService submit(Runnable task)`方法
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {...}
}
- 如果不指定,默认使用
ForkJoinPool.commonPool()
- 如果机器是单核的,则默认使用
ThreadPerTaskExecutor
,该类是一个内部类,每次执行execute都会创建一个新线程。
【代码示例 - RunAsync】
@Slf4j
public class RunAsyncDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> runAsyncFuture1 = CompletableFuture.runAsync(() -> {
log.info("do something");
});
CompletableFuture<Void> runAsyncFuture2 = CompletableFuture.runAsync(() -> {
log.info("do something");
}, Executors.newSingleThreadExecutor());
CompletableFuture<Void> runAsyncFuture3 = CompletableFuture.runAsync(() -> {
log.info("do something");
throw new RuntimeException();
});
TimeUnit.SECONDS.sleep(2);
log.info("future result:");
runAsyncFuture3.get(); // 这里必须要get才会抛出异常
}
}
23:17:57.783 [ForkJoinPool.commonPool-worker-2] INFO com.test.RunAsyncDemo - do something
23:17:57.783 [pool-1-thread-1] INFO com.test.RunAsyncDemo - do something
23:17:57.783 [ForkJoinPool.commonPool-worker-1] INFO com.test.RunAsyncDemo - do something
23:17:59.786 [main] INFO com.test.RunAsyncDemo - future result:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
at com.test.RunAsyncDemo.main(RunAsyncDemo.java:28)
...
【代码示例 - SupplyAsync】
@Slf4j
public class SupplyAsyncDemo {
private static final boolean errorFlag = true;
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> supplyAsyncFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("do something");
return "result";
});
log.info(supplyAsyncFuture1.get());
CompletableFuture<String> supplyAsyncFuture2 = CompletableFuture.supplyAsync(() -> {
log.info("do something");
return "result";
}, Executors.newSingleThreadExecutor());
log.info(supplyAsyncFuture2.get());
CompletableFuture<String> supplyAsyncFuture3 = CompletableFuture.supplyAsync(() -> {
log.info("do something");
if (errorFlag) throw new RuntimeException();
return "result";
});
TimeUnit.SECONDS.sleep(2);
log.info("future result:");
log.info(supplyAsyncFuture3.get()); // 这里必须要get才会抛出异常
}
}
23:14:35.583 [ForkJoinPool.commonPool-worker-1] INFO com.test.SupplyAsyncDemo - do something
23:14:35.585 [main] INFO com.test.SupplyAsyncDemo - result
23:14:35.586 [pool-1-thread-1] INFO com.test.SupplyAsyncDemo - do something
23:14:35.587 [main] INFO com.test.SupplyAsyncDemo - result
23:14:35.587 [ForkJoinPool.commonPool-worker-1] INFO com.test.SupplyAsyncDemo - do something
23:14:37.592 [main] INFO com.test.SupplyAsyncDemo - future result:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
at com.test.SupplyAsyncDemo.main(SupplyAsyncDemo.java:34)
二、异步回调
下述的多个方法,每个方法都有两个以Async结尾的方法,一个使用默认的Executor实现,一个使用指定的Executor实现
- 不以Async结尾的方法:由触发该任务的线程执行该任务。
- 以Async结尾的方法:由触发该任务的线程将任务提交到线程池,执行任务的线程跟触发任务的线程不一定是同一个。
- 没有指定 Executor:默认使用
ForkJoinPool.commonPool()
- 指定了 Executor:使用指定的 Executor
- 没有指定 Executor:默认使用
1、thenApply / thenApplyAsync
接收上一个任务的返回值作为Function函数的参数,有返回值。
// java.util.concurren.CompletableFuture
// 由触发该任务的线程执行该任务
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
// 默认使用ForkJoinPool.commonPool()
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) {
return uniApplyStage(asyncPool, fn);
}
// 可以指定线程池
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) {
return uniApplyStage(screenExecutor(executor), fn);
}
1)案例1
thenApply
案例。都不指定 Executors
@Slf4j
public class ThenApplyDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = cf1.thenApply((result) -> {
log.info("cf2 do something....");
result += 2;
return result;
});
log.info("cf1结果:" + cf1.get()); // 1
log.info("cf2结果:" + cf2.get()); // 3
}
}
23:30:47.444 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenApplyDemo - cf1 do something....
// 沿用触发该任务的线程
23:30:47.445 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenApplyDemo - cf2 do something....
23:30:47.445 [main] INFO com.test.ThenApplyDemo - cf1结果:1
23:30:47.445 [main] INFO com.test.ThenApplyDemo - cf2结果:3
2)案例2
thenApply
案例。调用任务指定 Executors,回调任务不指定 Executors
@Slf4j
public class ThenApplyDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
}, Executors.newFixedThreadPool(3));
CompletableFuture<Integer> cf2 = cf1.thenApply((result) -> {
log.info("cf2 do something....");
result += 2;
return result;
});
log.info("cf1结果:" + cf1.get()); // 1
log.info("cf2结果:" + cf2.get()); // 3
}
}
23:31:30.142 [pool-1-thread-1] INFO com.test.ThenApplyDemo - cf1 do something....
// 沿用触发该任务的线程
23:31:30.144 [pool-1-thread-1] INFO com.test.ThenApplyDemo - cf2 do something....
23:31:30.144 [main] INFO com.test.ThenApplyDemo - cf1结果:1
23:31:30.144 [main] INFO com.test.ThenApplyDemo - cf2结果:3
3)案例3
thenApplyAsync
案例。调用任务指定 Executors,回调任务不指定 Executors
@Slf4j
public class ThenApplyDemo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
}, Executors.newFixedThreadPool(3));
CompletableFuture<Integer> cf2 = cf1.thenApplyAsync((result) -> {
log.info("cf2 do something....");
result += 2;
return result;
});
log.info("cf1结果:" + cf1.get()); // 1
log.info("cf2结果:" + cf2.get()); // 3
}
}
23:32:30.244 [pool-1-thread-1] INFO com.test.ThenApplyDemo - cf1 do something....
// 默认使用`ForkJoinPool.commonPool()`
23:32:30.246 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenApplyDemo - cf2 do something....
23:32:30.246 [main] INFO com.test.ThenApplyDemo - cf1结果:1
23:32:30.246 [main] INFO com.test.ThenApplyDemo - cf2结果:3
4)案例4
thenApplyAsync
案例。都指定 Executors
@Slf4j
public class ThenApplyDemo4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
}, Executors.newFixedThreadPool(3));
CompletableFuture<Integer> cf2 = cf1.thenApplyAsync((result) -> {
log.info("cf2 do something....");
result += 2;
return result;
}, Executors.newFixedThreadPool(3));
log.info("cf1结果:" + cf1.get()); // 1
log.info("cf2结果:" + cf2.get()); // 3
}
}
23:33:01.813 [pool-1-thread-1] INFO com.test.ThenApplyDemo - cf1 do something....
// 使用指定的Executor
23:33:01.814 [pool-2-thread-1] INFO com.test.ThenApplyDemo - cf2 do something....
23:33:01.814 [main] INFO com.test.ThenApplyDemo - cf1结果:1
23:33:01.815 [main] INFO com.test.ThenApplyDemo - cf2结果:3
2、thenAccept / thenAcceptAsync
接收上一个任务的返回值作为Consumer函数的参数,无返回值。
// java.util.concurren.CompletableFuture
// 由触发该任务的线程执行该任务
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
// 默认使用ForkJoinPool.commonPool()
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
return uniAcceptStage(asyncPool, action);
}
// 可以指定线程池
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor) {
return uniAcceptStage(screenExecutor(executor), action);
}
案例
@Slf4j
public class ThenAcceptDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Void> cf2 = cf1.thenAccept((result) -> {
log.info("cf1 result is " + result);
});
TimeUnit.SECONDS.sleep(1);
}
}
23:58:05.771 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenAcceptDemo - cf1 do something....
23:58:05.773 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenAcceptDemo - cf1 result is 1
3、thenRun / thenRunAsync
当某个任务执行完成后执行的回调方法,无返回值
// java.util.concurren.CompletableFuture
// 由触发该任务的线程执行该任务
public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}
// 默认使用ForkJoinPool.commonPool()
public CompletableFuture<Void> thenRunAsync(Runnable action) {
return uniRunStage(asyncPool, action);
}
// 可以指定线程池
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor) {
return uniRunStage(screenExecutor(executor), action);
}
案例
@Slf4j
public class ThenRunDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
});
CompletableFuture<Void> cf2 = cf1.thenRun(() -> {
log.info("cf2 do something...."); // 等cf1执行完执行
});
TimeUnit.SECONDS.sleep(2);
}
}
23:59:01.862 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenRunDemo - cf1 do something....
23:59:02.869 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenRunDemo - cf2 do something....
4、whenComplete / whenCompleteAsync
whenComplete
是当某个任务执行完成后执行的回调方法,会将 执行结果
和 执行期间抛出的异常
传递给回调方法
- 调用任务 正常执行,对回调任务来说:
结果参数
:调用任务的结果异常参数
:nullget方法
:返回调用任务的结果
- 调用任务 异常执行,对回调任务来说:
结果参数
:null异常参数
:调用任务抛出的异常get方法
:抛出调用任务抛出的异常
// java.util.concurren.CompletableFuture
// 由触发该任务的线程执行该任务
public CompletableFuture<T> whenComplete(
BiConsumer<? super T, ? super Throwable> action) {
return uniWhenCompleteStage(null, action);
}
// 默认使用ForkJoinPool.commonPool()
public CompletableFuture<T> whenCompleteAsync(
BiConsumer<? super T, ? super Throwable> action) {
return uniWhenCompleteStage(asyncPool, action);
}
// 可以指定线程池
public CompletableFuture<T> whenCompleteAsync(
BiConsumer<? super T, ? super Throwable> action, Executor executor) {
return uniWhenCompleteStage(screenExecutor(executor), action);
}
1)正常执行
@Slf4j
public class WhenCompleteDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1正常执行
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = cf1.whenComplete((result, ex) -> {
log.info("cf1结果:" + result);
log.info("cf1异常:" + ex);
log.info("cf2 do something....");
});
// 返回cf1的结果
log.info("cf2结果:" + cf2.get());
}
}
19:35:41.890 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo1 - cf1 do something....
19:35:41.891 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo1 - cf1结果:1
19:35:41.891 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo1 - cf1异常:null
19:35:41.891 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo1 - cf2 do something....
19:35:41.891 [main] INFO com.test.WhenCompleteDemo1 - cf2结果:1
2)异常执行
@Slf4j
public class WhenCompleteDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1异常执行
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
int a = 1/0;
return 1;
});
CompletableFuture<Integer> cf2 = cf1.whenComplete((result, ex) -> {
log.info("cf1结果:" + result);
log.info("cf1异常:" + ex);
log.info("cf2 do something....");
});
// 抛出cf1抛出的异常
log.info("cf2结果:" + cf2.get());
}
}
19:38:46.366 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo2 - cf1 do something....
19:38:46.368 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo2 - cf1结果:null
19:38:46.368 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo2 - cf1异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
19:38:46.368 [ForkJoinPool.commonPool-worker-1] INFO com.test.WhenCompleteDemo2 - cf2 do something....
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
at com.test.WhenCompleteDemo2.main(WhenCompleteDemo2.java:24)
...
5、handle / handleAsync
handle
是当某个任务执行完成后执行的回调方法,会将 执行结果
和 执行期间抛出的异常
传递给回调方法
- 调用任务 正常执行,对回调任务来说:
结果参数
:调用任务的结果异常参数
:nullget方法
:返回handle回调的结果
- 调用任务 异常执行,对回调任务来说:
结果参数
:null异常参数
:调用任务抛出的异常get方法
:不会抛出调用任务的异常
// 由触发该任务的线程执行该任务
public <U> CompletableFuture<U> handle(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(null, fn);
}
// 默认使用ForkJoinPool.commonPool()
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(asyncPool, fn);
}
// 可以指定线程池
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
return uniHandleStage(screenExecutor(executor), fn);
}
1)正常执行
@Slf4j
public class HandleDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1正常执行
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = cf1.handle((result, ex) -> {
log.info("cf1结果:" + result);
log.info("cf1异常:" + ex);
log.info("cf2 do something....");
return 3;
});
// 返回cf2的结果
log.info("cf2结果:" + cf2.get());
}
}
19:59:40.994 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo1 - cf1 do something....
19:59:40.996 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo1 - cf1结果:1
19:59:40.996 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo1 - cf1异常:null
19:59:40.996 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo1 - cf2 do something....
19:59:40.996 [main] INFO com.test.HandleDemo1 - cf2结果:3
2)异常执行
@Slf4j
public class HandleDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1异常执行
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
int a = 1/0;
return 1;
});
CompletableFuture<Integer> cf2 = cf1.handle((result, ex) -> {
log.info("cf1结果:" + result);
log.info("cf1异常:" + ex);
log.info("cf2 do something....");
return 3;
});
// 返回cf2的结果
log.info("cf2结果:" + cf2.get());
}
}
20:00:20.659 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo2 - cf1 do something....
20:00:20.660 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo2 - cf1结果:null
20:00:20.660 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo2 - cf1异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
20:00:20.660 [ForkJoinPool.commonPool-worker-1] INFO com.test.HandleDemo2 - cf2 do something....
20:00:20.660 [main] INFO com.test.HandleDemo2 - cf2结果:3
6、exceptionally
将调用任务中 抛出的异常作为参数 传递到回调方法中。
- 调用任务正常执行:直接返回调用任务的执行结果,不执行exceptionally回调方法内部代码
- 调用任务异常执行:将异常作为参数传递给exceptionally回调方法,并执行exceptionally回调方法内部代码
public CompletableFuture<T> exceptionally(
Function<Throwable, ? extends T> fn) {
return uniExceptionallyStage(fn);
}
1)正常执行
@Slf4j
public class ExceptionallyDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1正常执行
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
// 内部不执行
CompletableFuture<Integer> cf2 = cf1.exceptionally((ex) -> {
log.info("cf1异常:" + ex);
log.info("cf2 do something....");
return 3;
});
// 返回cf1的结果
log.info("cf2结果:" + cf2.get());
}
}
20:14:55.423 [ForkJoinPool.commonPool-worker-1] INFO com.test.ExceptionallyDemo1 - cf1 do something....
20:14:55.424 [main] INFO com.test.ExceptionallyDemo1 - cf2结果:1
2)异常执行
@Slf4j
public class ExceptionallyDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1异常执行
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
int a = 1/0;
return 1;
});
// 内部执行
CompletableFuture<Integer> cf2 = cf1.exceptionally((ex) -> {
log.info("cf1异常:" + ex);
log.info("cf2 do something....");
return 3;
});
// 返回cf2的结果
log.info("cf2结果:" + cf2.get());
}
}
20:16:51.299 [ForkJoinPool.commonPool-worker-1] INFO com.test.ExceptionallyDemo2 - cf1 do something....
20:16:51.300 [ForkJoinPool.commonPool-worker-1] INFO com.test.ExceptionallyDemo2 - cf1异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
20:16:51.300 [ForkJoinPool.commonPool-worker-1] INFO com.test.ExceptionallyDemo2 - cf2 do something....
20:16:51.300 [main] INFO com.test.ExceptionallyDemo2 - cf2结果:3
三、组合处理
1、thenCombine / thenAcceptBoth / runAfterBoth
这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。
thenCombine
:将两个任务的执行结果作为函数入参,有返回值;thenAcceptBoth
:将两个任务的执行结果作为函数入参,无返回值;runAfterBoth
:没有入参,也没有返回值。
注意:两个任务中只要有一个执行异常,则调用get方法就会抛出该异常。
1)thenCombine 案例
@Slf4j
public class ThenCombineDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return 2;
});
CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2, (a, b) -> {
// 这里会阻塞2s
log.info("cf3 do something....");
return a + b;
});
log.info("cf3结果:" + cf3.get());
}
}
20:21:47.216 [ForkJoinPool.commonPool-worker-2] INFO com.test.ThenCombineDemo - cf2 do something....
20:21:47.216 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenCombineDemo - cf1 do something....
// 阻塞了2s
20:21:49.223 [ForkJoinPool.commonPool-worker-2] INFO com.test.ThenCombineDemo - cf3 do something....
20:21:49.223 [main] INFO com.test.ThenCombineDemo1 - cf3结果:3
2)thenAcceptBoth 案例
@Slf4j
public class ThenAcceptBothDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return 2;
});
CompletableFuture<Void> cf3 = cf1.thenAcceptBoth(cf2, (a, b) -> {
log.info("cf3 do something....");
log.info("a={}, b={}", a, b);
});
log.info("cf3结果:" + cf3.get());
}
}
20:27:21.025 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenAcceptBothDemo - cf1 do something....
20:27:21.025 [ForkJoinPool.commonPool-worker-2] INFO com.test.ThenAcceptBothDemo - cf2 do something....
// 阻塞了2s
20:27:23.030 [ForkJoinPool.commonPool-worker-2] INFO com.test.ThenAcceptBothDemo - cf3 do something....
20:27:23.030 [ForkJoinPool.commonPool-worker-2] INFO com.test.ThenAcceptBothDemo - a=1, b=2
20:27:23.033 [main] INFO com.test.ThenAcceptBothDemo - cf3结果:null
3)runAfterBoth 案例
@Slf4j
public class RunAfterBothDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return 2;
});
CompletableFuture<Void> cf3 = cf1.runAfterBoth(cf2, () -> {
log.info("cf3 do something....");
});
log.info("cf3结果:" + cf3.get());
}
}
20:29:17.640 [ForkJoinPool.commonPool-worker-1] INFO com.test.RunAfterBothDemo - cf1 do something....
20:29:17.640 [ForkJoinPool.commonPool-worker-2] INFO com.test.RunAfterBothDemo - cf2 do something....
// 阻塞了2s
20:29:19.649 [ForkJoinPool.commonPool-worker-2] INFO com.test.RunAfterBothDemo - cf3 do something....
20:29:19.650 [main] INFO com.test.RunAfterBothDemo - cf3结果:null
2、applyToEither / acceptEither / runAfterEither
这三个方法和上面一样也是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。
applyToEither
:将先完成任务的执行结果作为函数入参,有返回值;acceptEither
:将先完成任务的执行结果作为函数入参,无返回值;runAfterEither
:没有入参,也没有返回值。
1)applyToEither 案例
@Slf4j
public class ApplyToEitherDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return 2;
});
CompletableFuture<Integer> cf3 = cf1.applyToEither(cf2, (firstResult) -> {
log.info("cf3 do something....");
return firstResult;
});
log.info("cf3结果:" + cf3.get());
}
}
20:33:28.433 [ForkJoinPool.commonPool-worker-2] INFO com.test.ApplyToEitherDemo - cf2 do something....
20:33:28.433 [ForkJoinPool.commonPool-worker-1] INFO com.test.ApplyToEitherDemo - cf1 do something....
20:33:28.435 [ForkJoinPool.commonPool-worker-1] INFO com.test.ApplyToEitherDemo - cf3 do something....
20:33:28.435 [main] INFO com.test.ApplyToEitherDemo - cf3结果:1
2)acceptEither 案例
@Slf4j
public class ThenAcceptEitherDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return 2;
});
CompletableFuture<Void> cf3 = cf1.acceptEither(cf2, (firstResult) -> {
log.info("cf3 do something....");
log.info("firstResult={}", firstResult);
});
log.info("cf3结果:" + cf3.get());
}
}
20:35:11.357 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenAcceptEitherDemo - cf1 do something....
20:35:11.359 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenAcceptEitherDemo - cf3 do something....
20:35:11.357 [ForkJoinPool.commonPool-worker-2] INFO com.test.ThenAcceptEitherDemo - cf2 do something....
20:35:11.359 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenAcceptEitherDemo - firstResult=1
20:35:11.360 [main] INFO com.test.ThenAcceptEitherDemo - cf3结果:null
3)runAfterEither 案例
@Slf4j
public class RunAfterEitherDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return 1;
});
CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return 2;
});
CompletableFuture<Void> cf3 = cf1.runAfterEither(cf2, () -> {
log.info("cf3 do something....");
});
log.info("cf3结果:" + cf3.get());
}
}
20:36:36.994 [ForkJoinPool.commonPool-worker-2] INFO com.test.RunAfterEitherDemo - cf2 do something....
20:36:36.994 [ForkJoinPool.commonPool-worker-1] INFO com.test.RunAfterEitherDemo - cf1 do something....
20:36:36.996 [ForkJoinPool.commonPool-worker-1] INFO com.test.RunAfterEitherDemo - cf3 do something....
20:36:36.996 [main] INFO com.test.RunAfterEitherDemo - cf3结果:null
3、allOf
allOf
是多个任务都执行完成后才会执行
-
如果都是正常执行,则get返回null
-
只要有一个任务执行异常,执行get方法时就会抛出异常(没有异常的任务会正常执行)
但是要等所有方法执行完毕才抛出异常。
1)全部正常执行
@Slf4j
public class AllOfDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return "cf1 任务完成";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return "cf2 任务完成";
});
CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
log.info("cf3 do something....");
return "cf3 任务完成";
});
CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2, cf3);
log.info("allOf结果:" + cfAll.get());
}
}
20:38:54.369 [ForkJoinPool.commonPool-worker-3] INFO com.test.AllOfDemo1 - cf3 do something....
20:38:54.369 [ForkJoinPool.commonPool-worker-1] INFO com.test.AllOfDemo1 - cf1 do something....
20:38:54.369 [ForkJoinPool.commonPool-worker-2] INFO com.test.AllOfDemo1 - cf2 do something....
// 阻塞2s
20:38:56.372 [main] INFO com.test.AllOfDemo1 - allOf结果:null
2)存在异常
@Slf4j
public class AllOfDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// cf1发生异常,并且异常比其余两个任务先发生
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
int a = 1 / 0;
return "cf1 任务完成";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return "cf2 任务完成";
});
CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
log.info("cf3 do something....");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
return "cf3 任务完成";
});
CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2, cf3);
try {
log.info("allOf结果:" + cfAll.get());
} catch (Exception e) {
log.error("allOf异常:", e);
}
}
}
20:46:34.074 [ForkJoinPool.commonPool-worker-1] INFO com.test.AllOfDemo2 - cf1 do something....
// 没有异常的任务会正常执行
20:46:34.074 [ForkJoinPool.commonPool-worker-3] INFO com.test.AllOfDemo2 - cf3 do something....
20:46:34.074 [ForkJoinPool.commonPool-worker-2] INFO com.test.AllOfDemo2 - cf2 do something....
// 等所有任务执行完毕才抛出异常
20:46:37.086 [main] ERROR com.test.AllOfDemo2 - allOf异常:
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
at com.test.AllOfDemo2.main(AllOfDemo2.java:40)
4、anyOf
anyOf
是多个任务只要有一个任务执行完成就会执行
- 如果都是正常执行,则get返回执行完成的任务的结果。(返回后不会再执行其余任务)
- 如果没有任何任务完成时发生异常,则执行get方法时就会抛出异常
- 如果发生异常时已经有任务完成,则执行get方法时不会抛出异常
1)全部正常执行
@Slf4j
public class AnyOfDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf1 finish....");
return "cf1 result";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf2 finish....");
return "cf2 result";
});
CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
log.info("cf3 do something....");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf3 finish....");
return "cf3 result";
});
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(cf1, cf2, cf3);
log.info("anyOf result:" + anyOf.get());
}
}
20:52:40.925 [ForkJoinPool.commonPool-worker-1] INFO com.test.AnyOfDemo1 - cf1 do something....
20:52:40.925 [ForkJoinPool.commonPool-worker-3] INFO com.test.AnyOfDemo1 - cf3 do something....
20:52:40.925 [ForkJoinPool.commonPool-worker-2] INFO com.test.AnyOfDemo1 - cf2 do something....
20:52:41.932 [ForkJoinPool.commonPool-worker-1] INFO com.test.AnyOfDemo1 - cf1 finish....
// cf1任务完成,cf2和cf3就不会执行了
20:52:41.933 [main] INFO com.test.AnyOfDemo1 - anyOf result:cf1 result
2)异常时,没有任务完成
@Slf4j
public class AnyOfDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
int a = 1/0; // 异常(还没有任务完成)
log.info("cf1 finish....");
return "cf1 result";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf2 finish....");
return "cf2 result";
});
CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
log.info("cf3 do something....");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf3 finish....");
return "cf3 result";
});
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(cf1, cf2, cf3);
log.info("anyOf result:" + anyOf.get());
}
}
20:55:37.652 [ForkJoinPool.commonPool-worker-2] INFO com.test.AnyOfDemo2 - cf2 do something....
20:55:37.652 [ForkJoinPool.commonPool-worker-1] INFO com.test.AnyOfDemo2 - cf1 do something....
20:55:37.652 [ForkJoinPool.commonPool-worker-3] INFO com.test.AnyOfDemo2 - cf3 do something....
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
at com.test.AnyOfDemo2.main(AnyOfDemo2.java:46)
...
3)异常时,有任务完成
@Slf4j
public class AnyOfDemo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf1 finish....");
return "cf1 result";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
log.info("cf2 do something....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
int a = 1/0; // 异常(cf1任务已经完成)
log.info("cf2 finish....");
return "cf2 result";
});
CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
log.info("cf3 do something....");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// Todo Handle Exception
}
log.info("cf3 finish....");
return "cf3 result";
});
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(cf1, cf2, cf3);
log.info("anyOf result:" + anyOf.get());
}
}
20:56:24.220 [ForkJoinPool.commonPool-worker-3] INFO com.test.AnyOfDemo3 - cf3 do something....
20:56:24.220 [ForkJoinPool.commonPool-worker-2] INFO com.test.AnyOfDemo3 - cf2 do something....
20:56:24.220 [ForkJoinPool.commonPool-worker-1] INFO com.test.AnyOfDemo3 - cf1 do something....
20:56:25.229 [ForkJoinPool.commonPool-worker-1] INFO com.test.AnyOfDemo3 - cf1 finish....
20:56:25.230 [main] INFO com.test.AnyOfDemo3 - anyOf result:cf1 result
cf2发生异常时,cf1已经完成任务,所以异常不会抛出
5、thenCompose
thenCompose
方法会在某个任务执行完成后,将该任务的执行结果作为方法入参然后执行指定的方法
案例
@Slf4j
public class ThenComposeDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
log.info("cf1 do something....");
return "cf1 result";
});
CompletableFuture<String> cf2 = cf1.thenCompose((result) -> {
log.info("cf2 do something....");
return CompletableFuture.supplyAsync(() -> result);
});
log.info("cf1结果:" + cf1.get());
log.info("cf2结果:" + cf2.get());
}
}
21:01:04.228 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenComposeDemo - cf1 do something....
21:01:04.229 [ForkJoinPool.commonPool-worker-1] INFO com.test.ThenComposeDemo - cf2 do something....
21:01:04.229 [main] INFO com.test.ThenComposeDemo - cf1结果:cf1 result
21:01:04.230 [main] INFO com.test.ThenComposeDemo - cf2结果:cf1 result
6、thenCompose 对比 thenAccept
// java.util.concurren.CompletableFuture
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(null, fn);
}
可以看到,两个方法的返回值都是CompletionStage<U>
,不同之处在于它们的传入参数fn
.
- 对于
thenApply
,fn
函数是一个对一个已完成的stage 或 CompletableFuture的返回值进行计算、操作; - 对于
thenCompose
,fn
函数是对另一个CompletableFuture进行计算、操作。
案例
public class CompareTest {
public static void main(String[] args) {
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> 100)
.thenApply(num -> num + " to String");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> 100)
.thenCompose(num -> CompletableFuture.supplyAsync(() -> num + " to String"));
System.out.println(f1.join()); // 100 to String
System.out.println(f2.join()); // 100 to String
}
}
例子中,thenApply
和thenCompose
都是将一个CompletableFuture<Integer>
转换为CompletableFuture<String>
。
- 不同的是,
thenApply
中的传入函数的返回值是String
, - 而
thenCompose
的传入函数的返回值是CompletableFuture<String>
。
有点像stream中的map
和flatMap
。
回想我们做过的二维数组转一维数组,使用stream().flatMap
映射时,我们是把流中的每个数据(数组)又展开为了流。