CompletableFuture线程编排
1. 线程编排
1.1 线程编排场景
asyncTool作者总结的线程编排场景
1.2 使用CompletableFuture实现
假设要编排的任务都是带出参入参的,并且耗时都是1S,从taskA到taskH,每个都是类似下面的代码
public static String taskA(String param) {
System.out.println("taskA start, receive param:" + param);
sleep(1);
System.out.println("taskA end");
return "taskA";
}
public static String taskB(String param) {
System.out.println("taskB start, receive param:" + param);
sleep(1);
System.out.println("taskB end");
return "taskB";
}
1) ABC串行
@Test
public void seq() throws ExecutionException, InterruptedException, TimeoutException {
String param = "apple";
CompletableFuture<String> seqCF = CompletableFuture.supplyAsync(() -> {
return taskA(param);
}).thenApply((a) -> {
return taskB(a);
}).thenApply((c) -> {
return taskC(c);
});
String res = seqCF.get(5, TimeUnit.SECONDS);
System.out.println("finally " + res);
}
输出:
taskA start, receive param:apple
taskA end
taskB start, receive param:taskA
taskB end
taskC start, receive param:taskB
taskC end
finally taskC
2)ABC并行
@Test
public void parallel() throws ExecutionException, InterruptedException, TimeoutException {
String paramA = "apple A", paramB = "apple B", paramC = "apple C";
CompletableFuture<String> taf = CompletableFuture.supplyAsync(() -> taskA(paramA));
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(paramB));
CompletableFuture<String> tcf = CompletableFuture.supplyAsync(() -> taskC(paramC));
//此时主线程阻塞等待3个子线程执行完毕
CompletableFuture.allOf(taf, tbf, tcf).get(5, TimeUnit.SECONDS);
}
结果
taskA start, receive param:apple A
taskB start, receive param:apple B
taskC start, receive param:apple C
taskB end
taskA end
taskC end
3)A结束BC并行
@Test
public void depend_a_parallel_b_c() throws ExecutionException, InterruptedException, TimeoutException {
String paramA = "apple A";
CompletableFuture<String> taf = CompletableFuture.supplyAsync(() -> taskA(paramA));
String resA = taf.get(2, TimeUnit.SECONDS);
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(resA));
CompletableFuture<String> tcf = CompletableFuture.supplyAsync(() -> taskC(resA));
CompletableFuture.allOf(tbf, tcf).get(2, TimeUnit.SECONDS);
}
结果
taskA start, receive param:apple A
taskA end
taskB start, receive param:taskA
taskC start, receive param:taskA
taskC end
taskB end
4)BC都执行完毕后执行A
A依赖BC的执行结果,且A依赖的任务数量不能超过2个,超过2个必须得用allOf
/**
* BC都执行完毕后执行A
* A依赖BC的执行结果,且A依赖的任务数量不能超过2个,超过2个必须得用allOf
*/
@Test
public void depend_parallel_b_c_then_a() throws ExecutionException, InterruptedException, TimeoutException {
String paramB = "apple B", paramC = "apple C";
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(paramB));
CompletableFuture<String> tcf = CompletableFuture.supplyAsync(() -> taskC(paramC));
String resA = tbf.thenCombine(tcf, (resB, resC) -> {
System.out.println(resC);
return taskA(resB);
}).get(3, TimeUnit.SECONDS);
System.out.println(resA);
}
当A的前置依赖超过2个时,用此方法
/**
* BC都执行完毕后执行A
* 当A的前置依赖超过2个时,用此方法
*/
@Test
public void depend_parallel_b_c_d_then_a() throws ExecutionException, InterruptedException, TimeoutException {
String paramA = "apple A", paramB = "apple B", paramC = "apple C", paramD = "apple D";
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(paramB));
CompletableFuture<String> tcf = CompletableFuture.supplyAsync(() -> taskC(paramC));
CompletableFuture<String> tdf = CompletableFuture.supplyAsync(() -> taskD(paramD));
String resA = CompletableFuture.allOf(tbf, tcf, tdf).thenApply((resNull) -> {
//allOf时,返回的结果是null,无法将结果传给taskA
System.out.println(resNull);
return taskA(paramA);
}).get(3, TimeUnit.SECONDS);
System.out.println(resA);
System.out.println("end");
//如果A要依赖BCD的结果
CompletableFuture.allOf(tbf, tcf, tdf).get(3,TimeUnit.SECONDS);
String resB = tbf.join();
String resC = tcf.join();
String resD = tdf.join();
taskA(resB);
}
5)BC任意一个执行完后执行A
和上面4)一样,先来只有2个任务,且依赖参数传递的
@Test
public void depend_parallel_b_c_any_of_a() throws ExecutionException, InterruptedException, TimeoutException {
String paramB = "apple B", paramC = "apple C";
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(paramB));
String resA = CompletableFuture.supplyAsync(() -> taskC(paramC)).applyToEither(tbf, (resAny) -> {
return taskA(resAny);
}).get(3, TimeUnit.SECONDS);
System.out.println(resA);
}
结果:
taskC start, receive param:apple C
taskB start, receive param:apple B
taskC end
taskB end
taskA start, receive param:taskC
taskA end
taskA
超过2个任务的情况
@Test
public void depend_parallel_b_c_d_any_of_a() throws ExecutionException, InterruptedException, TimeoutException {
String paramB = "apple B", paramC = "apple C", paramD = "apple D";
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(paramB));
CompletableFuture<String> tcf = CompletableFuture.supplyAsync(() -> taskC(paramC));
CompletableFuture<String> tdf = CompletableFuture.supplyAsync(() -> taskD(paramD));
CompletableFuture.anyOf(tbf, tcf, tdf).thenApply((resAny) -> {
return taskA((String) resAny);
}).get(3, TimeUnit.SECONDS);
}
结果:
taskC start, receive param:apple C
taskB start, receive param:apple B
taskD start, receive param:apple D
taskD end
taskC end
taskB end
taskA start, receive param:taskD
taskA end
6)图6中复杂场景
A先执行,将A的结果传给BC两个任务,BC两个任务并行执行,DE分别依赖BC的执行结果作为入参。BD和CE两个串行的任务并行执行完成后,将结果给到F
@Test
public void complex_pic_6_v2() throws ExecutionException, InterruptedException, TimeoutException {
String paramA = "apple A";
CompletableFuture<String> taf = CompletableFuture.supplyAsync(() -> taskA(paramA));
CompletableFuture<String> seqBD = taf.thenApplyAsync(CompletableFutureDemo::taskB).thenApply(CompletableFutureDemo::taskD);
CompletableFuture<String> seqCE = taf.thenApplyAsync(CompletableFutureDemo::taskC).thenApply(CompletableFutureDemo::taskE);
String resF = seqBD.thenCombine(seqCE, (seqBDRes, seqCERes) -> {
System.out.printf("seqBDRes: %s seqCERes: %s\n", seqBDRes, seqCERes);
return taskF(seqCERes);
}).get(5, TimeUnit.SECONDS);
System.out.println(resF);
}
结果:
为方便分析,增加了时间戳打印
1681831591358 ForkJoinPool.commonPool-worker-3 taskA start, receive param:apple A
taskA end
1681831592359 ForkJoinPool.commonPool-worker-5 taskB start, receive param:taskA
1681831592359 ForkJoinPool.commonPool-worker-7 taskC start, receive param:taskA
taskB end
taskC end
1681831593367 ForkJoinPool.commonPool-worker-5 taskD start, receive param:taskB
1681831593367 ForkJoinPool.commonPool-worker-7 taskE start, receive param:taskC
taskD end
taskE end
seqBDRes: taskD seqCERes: taskE
1681831594369 ForkJoinPool.commonPool-worker-7 taskF start, receive param:taskE
taskF end
taskF
7)图7中复杂场景
@Test
public void complex_pic_7() throws ExecutionException, InterruptedException, TimeoutException {
String paramA = "apple A", paramD = "apple D";
CompletableFuture<String> seqDEG = CompletableFuture.supplyAsync(() -> taskD(paramD)).thenApply(CompletableFutureDemo::taskE).thenApply(CompletableFutureDemo::taskG);
CompletableFuture<String> taf = CompletableFuture.supplyAsync(() -> taskA(paramA));
String resA = taf.get(2, TimeUnit.SECONDS);
CompletableFuture<String> tbf = CompletableFuture.supplyAsync(() -> taskB(resA));
CompletableFuture<String> tcf = CompletableFuture.supplyAsync(() -> taskC(resA));
//并行执行BC任务,并获取BC任务的执行结果,作为F任务的入参
CompletableFuture<String> tff = tbf.thenCombine(tcf, (resB, resC) -> {
System.out.printf("resB: %s resC: %s\n", resB, resC);
return taskF(resC);
});
String resH = seqDEG.thenCombine(tff, (seqDEGRes, resF) -> {
System.out.printf("seqDEGRes: %s resF: %s\n", seqDEGRes, resF);
return taskH(resF);
}).get(5, TimeUnit.SECONDS);
CompletableFuture.supplyAsync(() -> taskC(resA));
System.out.println(resH);
}
结果
1681747665085 taskD start, receive param:apple D
1681747665087 taskA start, receive param:apple A
taskA end
taskD end
1681747666092 taskE start, receive param:taskD
1681747666093 taskB start, receive param:taskA
1681747666094 taskC start, receive param:taskA
taskC end
taskE end
taskB end
1681747667103 taskG start, receive param:taskE
resB: taskB resC: taskC
1681747667103 taskF start, receive param:taskC
taskG end
taskF end
seqDEGRes: taskG resF: taskF
1681747668114 taskH start, receive param:taskF
taskH end
taskH
1.2 子线程异常处理&超时等待
异常处理有3种API:handle、whenComplete和exceptionally
当发生异常时,原始异常是CompletionException ,经过whenComplete处理后,返回ExecutionException
public static CompletableFuture handle(int a, int b) {
CompletableFuture<Integer> resF = CompletableFuture.supplyAsync(() -> a / b).handle((result, ex) -> {
if (null != ex) {
System.out.println(ex.getMessage());
return 0;
} else {
return result;
}
});
return resF;
}
public static CompletableFuture whenComplete(int a, int b) {
CompletableFuture<Integer> resF = CompletableFuture.supplyAsync(() -> a / b).whenComplete((result, ex) -> {
if (null != ex) {
System.out.println("whenComplete error:\t" + ex.getMessage());
}
});
return resF;
}
public static CompletableFuture exceptionally(int a, int b) {
CompletableFuture<Integer> resF = CompletableFuture.supplyAsync(() -> a / b).exceptionally(ex -> {
System.out.println("ex:\t" + ex.getMessage());
return 0;
});
return resF;
}
@Test
public void testExceptHandle() throws ExecutionException, InterruptedException {
System.out.println("success:\t" + handle(6, 3).get());
System.out.println("exception:\t" + handle(6, 0).get());
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
try {
System.out.println("success:\t" + whenComplete(6, 3).get());
System.out.println("exception:\t" + whenComplete(6, 0).get());
} catch (CompletionException e1){
System.out.println("CompletionException");
e1.printStackTrace();
}catch (ExecutionException e2){
System.out.println("ExecutionException");
e2.printStackTrace();
} catch (Exception exception) {
System.out.println("catch====" + exception.getMessage());
}
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
System.out.println("success:\t" + exceptionally(6, 3).get());
System.out.println("exception:\t" + exceptionally(6, 0).get());
}
结果:
success: 2
java.lang.ArithmeticException: / by zero
exception: 0
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
success: 2
whenComplete error: java.lang.ArithmeticException: / by zero
ExecutionException
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999)
at com.exercise.thread.future.FutureExceptionTest.testC(FutureExceptionTest.java:83)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.ArithmeticException: / by zero
at com.exercise.thread.future.FutureExceptionTest.lambda$whenComplete$2(FutureExceptionTest.java:31)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1700)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
at java.base/java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
success: 2
ex: java.lang.ArithmeticException: / by zero
exception: 0
1.4 传参-log的traceId
先买个坑,后面专门写一篇
1.5 存在的缺陷,对比asyncTool
2.线程池
2.1 forkjoinPool
2.1 如何自定义线程池
2.1 拒绝策略选择,父子线程问题
3. 线程数量问题
参考
https://juejin.cn/post/7022086461386129415#heading-34