CompletableFuture线程编排

1. 线程编排

1.1 线程编排场景

asyncTool作者总结的线程编排场景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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值