CompletableFuture 使用教程

CompletableFuture实现了CompletionStage接口和Future接口,CompletionStage是对Future的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。

Future没有提供通知机制,Future是否执行完任务需要通过轮询isDone这个方法查询执行结果或者调用get()方法阻塞任务执行,CompletionStage解决了该问题,前一个任务执行成功后可以自动触发下一个任务的执行,中间无需等待。

1. 创建异步任务

supplyAsync方法

//使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
//自定义线程,根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

runAsync方法

//使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable) 
//自定义线程,根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable,  Executor executor)
  • supplyAsync执行CompletableFuture任务,支持返回值
  • runAsync执行CompletableFuture任务,没有返回值。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<Void> runFuture =
            CompletableFuture.runAsync(() -> userService.compute("runAsync"), fishExecutor);
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("supplyAsync"), fishExecutor);
        System.out.println(runFuture.join());
        System.out.println(supplyFuture.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

    }
}

join与get的区别

  • 相同点
    1.join()和get()方法都是用来获取CompletableFuture异步之后的返回值

  • 不同点
    1.join()方法抛出的是RuntimeException异常,不需要特殊处理
    2.get()方法抛出的是需要手动处理的异常,例如ExecutionException, InterruptedException,需要抛出或者try-catch

  • 结果打印

16:45:22.054 [fish] INFO com.example.mavendemo.Test$UserService - runAsync
16:45:22.054 [fish] INFO com.example.mavendemo.Test$UserService - supplyAsync
null
supplyAsync

2. 任务异步回调

1. thenRun/thenRunAsync

public CompletableFuture<Void> thenRun(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action);
  • thenRun/thenRunAsync作用是做完第一个任务后,再做第二个任务(Runnable action)。前一个任务执行结束后,执行回调方法(Runnable action);但是两个任务之间没有参数传递,第二个任务也没有返回值
  • thenRun 和 thenRunAsync 的区别:

thenRun执行第二个任务的线程池和第一个任务是同一个,或者thenRunAsync传入的线程池
thenRunAsync执行第二个任务使用的是ForkJoin线程池,或者你传入的线程池

    public CompletableFuture<Void> thenRun(Runnable action) {
        return uniRunStage(null, action);
    }

    public CompletableFuture<Void> thenRunAsync(Runnable action) {
        return uniRunStage(asyncPool, action);
    }

    public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor) {
        return uniRunStage(screenExecutor(executor), action);
    }    

package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test2 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("第一个任务"), fishExecutor);
        CompletableFuture<Void> voidCompletableFuture1 = supplyFuture.thenRun(() -> userService.noResult("第二个任务"));
        CompletableFuture<Void> voidCompletableFuture2 = supplyFuture.thenRunAsync(() -> userService.noResult("第三个任务"));
        System.out.println(voidCompletableFuture1.join());
        System.out.println(voidCompletableFuture2.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public void noResult(String message) {
            log.info(message);
        }

    }
}

  • 打印,第二个任务和第一个任务使用fish线程
16:45:48.526 [fish] INFO com.example.mavendemo.Test2$UserService - 第一个任务
16:45:48.529 [fish] INFO com.example.mavendemo.Test2$UserService - 第二个任务
null
16:45:48.530 [ForkJoinPool.commonPool-worker-9] INFO com.example.mavendemo.Test2$UserService - 第三个任务
null

  • 加入第四任务,在看任务使用的线程
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test2 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static ExecutorService catExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "cat"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("第一个任务"), fishExecutor);
        CompletableFuture<Void> voidCompletableFuture1 = supplyFuture.thenRun(() -> userService.noResult("第二个任务"));
        CompletableFuture<Void> voidCompletableFuture2 = supplyFuture.thenRunAsync(() -> userService.noResult("第三个任务"));
        CompletableFuture<Void> voidCompletableFuture3 =
            supplyFuture.thenRunAsync(() -> userService.noResult("第四个任务"), catExecutor);
        System.out.println(voidCompletableFuture1.join());
        System.out.println(voidCompletableFuture2.join());
        System.out.println(voidCompletableFuture3.join());
        fishExecutor.shutdown();
        catExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public void noResult(String message) {
            log.info(message);
        }

    }
}

  • 打印,第二个任务和第四个任务使用cat线程,第三个任务使用ForkJoinPool线程
16:47:34.132 [fish] INFO com.example.mavendemo.Test2$UserService - 第一个任务
16:47:34.134 [cat] INFO com.example.mavendemo.Test2$UserService - 第四个任务
16:47:34.134 [cat] INFO com.example.mavendemo.Test2$UserService - 第二个任务
null
16:47:34.135 [ForkJoinPool.commonPool-worker-9] INFO com.example.mavendemo.Test2$UserService - 第三个任务
null
null

2. thenAccept/thenAcceptAsync

  • 第一个任务执行结束后,执行第二个回调方法任务,会将第一个任务的执行结果作为入参,传递到回调方法中,但回调方法没有返回值
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test3 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<Void> voidCompletableFuture1 =
            supplyFuture.thenAccept((a) -> userService.noResult("传入了前一个任务的结果:" + a));
        CompletableFuture<Void> voidCompletableFuture2 =
            supplyFuture.thenAcceptAsync((a) -> userService.noResult("传入了前一个任务的结果:" + a));
        System.out.println(voidCompletableFuture1.join());
        System.out.println(voidCompletableFuture2.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public void noResult(String message) {
            log.info(message);
        }

    }
}

  • 打印
17:01:09.637 [fish] INFO com.example.mavendemo.Test3$UserService - 这是第一个任务
17:01:09.639 [fish] INFO com.example.mavendemo.Test3$UserService - 传入了前一个任务的结果:这是第一个任务
17:01:09.639 [ForkJoinPool.commonPool-worker-9] INFO com.example.mavendemo.Test3$UserService - 传入了前一个任务的结果:这是第一个任务
null
null

3. thenApply/thenApplyAsync

  • 第一个任务执行完成后,执行第二个任务的回调方法,将第一个任务的执行结果作为入参,传递到回调方法中,而且回调方法有返回值
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test4 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> voidCompletableFuture1 =
            supplyFuture.thenApply((a) -> userService.compute("传入了前一个任务的结果:" + a));
        CompletableFuture<String> voidCompletableFuture2 =
            supplyFuture.thenApplyAsync((a) -> userService.compute("传入了前一个任务的结果:" + a));
        System.out.println(voidCompletableFuture1.join());
        System.out.println(voidCompletableFuture2.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public void noResult(String message) {
            log.info(message);
        }

    }
}

  • 打印
17:06:08.946 [fish] INFO com.example.mavendemo.Test4$UserService - 这是第一个任务
17:06:08.948 [fish] INFO com.example.mavendemo.Test4$UserService - 传入了前一个任务的结果:这是第一个任务
传入了前一个任务的结果:这是第一个任务
17:06:08.948 [ForkJoinPool.commonPool-worker-9] INFO com.example.mavendemo.Test4$UserService - 传入了前一个任务的结果:这是第一个任务
传入了前一个任务的结果:这是第一个任务

4. exceptionally

  • 任务执行异常时,执行回调方法;并且将抛出的异常作为参数,传到回调方法
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test5 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.exceptionMethod("这是第一个任务"), fishExecutor);
        CompletableFuture<String> exceptionally = supplyFuture.exceptionally(o -> userService.catchException(o));
        System.out.println(exceptionally.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String exceptionMethod(String message) {
            log.info(message);
            throw new RuntimeException(message);
        }

        public String catchException(Object o) {
            RuntimeException e = (RuntimeException)o;
            log.error("{}", e);
            return e.getMessage();
        }

    }
}

  • 打印
17:37:53.901 [fish] INFO com.example.mavendemo.Test5$UserService - 这是第一个任务
17:37:53.907 [fish] ERROR com.example.mavendemo.Test5$UserService - {}
java.util.concurrent.CompletionException: java.lang.RuntimeException: 这是第一个任务
	at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
	at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: 这是第一个任务
	at com.example.mavendemo.Test5$UserService.exceptionMethod(Test5.java:27)
	at com.example.mavendemo.Test5.lambda$main$1(Test5.java:16)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
	... 3 common frames omitted
java.lang.RuntimeException: 这是第一个任务

5. whenComplete/whenCompleteAsync

  • 第一个任务执行结束后,执行第二个回调方法任务,把上一级任务执行的结果和异常信息作为入参传入回调方法;并且whenComplete方法返回的CompletableFuture的result是上个任务的结果。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test6 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> voidCompletableFuture1 =
            supplyFuture.whenComplete((a,throwable) -> userService.compute("传入了前一个任务的结果:" + a));
        CompletableFuture<String> voidCompletableFuture2 =
            supplyFuture.whenCompleteAsync((a,throwable) -> userService.compute("传入了前一个任务的结果:" + a));
        System.out.println(voidCompletableFuture1.join());
        System.out.println(voidCompletableFuture2.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public void noResult(String message) {
            log.info(message);
        }

    }
}

  • 打印
17:49:36.135 [fish] INFO com.example.mavendemo.Test6$UserService - 这是第一个任务
17:49:36.137 [fish] INFO com.example.mavendemo.Test6$UserService - 传入了前一个任务的结果:这是第一个任务
这是第一个任务
17:49:36.137 [ForkJoinPool.commonPool-worker-9] INFO com.example.mavendemo.Test6$UserService - 传入了前一个任务的结果:这是第一个任务
这是第一个任务

6. handle/handleAsync

  • 第一个任务执行结束后,执行第二个回调方法任务,把上一级任务执行的结果和异常信息作为入参传入回调方法;并且whenComplete方法返回的CompletableFuture的result是回调方法的结果。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test7 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> supplyFuture =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> voidCompletableFuture1 =
            supplyFuture.handle((a,throwable) -> userService.compute("传入了前一个任务的结果:" + a));
        CompletableFuture<String> voidCompletableFuture2 =
            supplyFuture.handleAsync((a,throwable) -> userService.compute("传入了前一个任务的结果:" + a));
        System.out.println(voidCompletableFuture1.join());
        System.out.println(voidCompletableFuture2.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public void noResult(String message) {
            log.info(message);
        }

    }
}

  • 打印
17:52:20.197 [fish] INFO com.example.mavendemo.Test7$UserService - 这是第一个任务
17:52:20.199 [fish] INFO com.example.mavendemo.Test7$UserService - 传入了前一个任务的结果:这是第一个任务
传入了前一个任务的结果:这是第一个任务
17:52:20.199 [ForkJoinPool.commonPool-worker-9] INFO com.example.mavendemo.Test7$UserService - 传入了前一个任务的结果:这是第一个任务
传入了前一个任务的结果:这是第一个任务

3. 多任务组合

1. AND

  • thenCombine / thenAcceptBoth / runAfterBoth都表示将两个CompletableFuture组合起来,只有这两个都正常执行结束,才会执行回调任务。
  • 区别在于:
    thenCombine/thenCombineAsync: 会将两个任务的执行结果作为入参,传到指定方法中,且有返回值
    thenAcceptBoth/thenAcceptBothAsync: 会将两个任务的执行结果作为入参,传到指定方法中,且无返回值
    runAfterBoth/runAfterBothAsync: 不会把执行结果当做入参,且没有返回值。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test8 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> first =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> second =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第二个任务"), fishExecutor);
        CompletableFuture<String> thenCombine = first.thenCombine(second, (s, s2) -> userService.combine(s, s2));
        System.out.println(thenCombine.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public String combine(String first, String second) {
            log.info("这是combine方法");
            return first + second;
        }

    }
}

  • 打印
09:49:38.621 [fish] INFO com.example.mavendemo.Test8$UserService - 这是第二个任务
09:49:38.621 [fish] INFO com.example.mavendemo.Test8$UserService - 这是第一个任务
09:49:38.624 [fish] INFO com.example.mavendemo.Test8$UserService - 这是combine方法
这是第一个任务这是第二个任务

2. OR

  • applyToEither / acceptEither / runAfterEither 都表示:将两个CompletableFuture组合起来,只要其中一个执行结束,就会执行回调任务。
  • 区别在于:
    applyToEither/applyToEitherAsync: 会将已经执行完成的任务的结果作为入参,传到指定方法中,且有返回值
    acceptEither/acceptEitherAsync: 会将已经执行完成的任务的结果作为入参,传到指定方法中,且无返回值
    runAfterEither/runAfterEitherAsync: 不会把执行结果当做入参,且没有返回值。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test9 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> first =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> second =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第二个任务"), fishExecutor);
        CompletableFuture<String> applyToEither = first.applyToEither(second, s -> userService.either(s));
        System.out.println(applyToEither.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public String either(String result) {
            log.info("这是either方法");
            return result;
        }

    }
}

  • 打印
10:02:51.523 [fish] INFO com.example.mavendemo.Test9$UserService - 这是第一个任务
10:02:51.523 [fish] INFO com.example.mavendemo.Test9$UserService - 这是第二个任务
10:02:51.526 [fish] INFO com.example.mavendemo.Test9$UserService - 这是either方法
这是第一个任务
  • 也可能打印
10:03:50.381 [fish] INFO com.example.mavendemo.Test9$UserService - 这是第二个任务
10:03:50.381 [fish] INFO com.example.mavendemo.Test9$UserService - 这是第一个任务
10:03:50.383 [fish] INFO com.example.mavendemo.Test9$UserService - 这是either方法
这是第二个任务

3. AllOf

  • 所有任务都执行完成后,才执行 allOf 返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture执行get或join方法,会抛出异常;如果都是正常执行,则返回null。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test10 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) {
        CompletableFuture<String> first =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> second =
            CompletableFuture.supplyAsync(() -> userService.error("这是第二个任务"), fishExecutor);

        CompletableFuture<Void> allOfFuture =
            CompletableFuture.allOf(first, second).whenComplete((unused, throwable) -> {
                System.out.println(unused);
                System.out.println(throwable.getMessage());
                System.out.println("finish");
            });
        System.out.println(allOfFuture.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

        public String error(String message) {
            log.info(message);
            throw new RuntimeException("出错了");
        }
    }
}

  • 打印
10:41:43.120 [fish] INFO com.example.mavendemo.Test10$UserService - 这是第二个任务
10:41:43.120 [fish] INFO com.example.mavendemo.Test10$UserService - 这是第一个任务
null
java.lang.RuntimeException: 出错了
finish
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.RuntimeException: 出错了
	at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
	at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: 出错了
	at com.example.mavendemo.Test10$UserService.computeError(Test10.java:40)
	at com.example.mavendemo.Test10.lambda$main$2(Test10.java:18)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
	... 3 more

4. AnyOf

  • 任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行get或join方法,返回执行完任务的结果。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test11 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> first =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第一个任务"), fishExecutor);
        CompletableFuture<String> second =
            CompletableFuture.supplyAsync(() -> userService.compute("这是第二个任务"), fishExecutor);

        CompletableFuture<Object> anyOfFuture =
            CompletableFuture.anyOf(second, first).whenComplete((unused, throwable) -> {
                System.out.println(unused);
                System.out.println("finish");
            });
        System.out.println(anyOfFuture.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public String compute(String message) {
            log.info(message);
            return message;
        }

    }
}

  • 打印
13:15:46.529 [fish] INFO com.example.mavendemo.Test11$UserService - 这是第二个任务
13:15:46.529 [fish] INFO com.example.mavendemo.Test11$UserService - 这是第一个任务
这是第二个任务
finish
这是第二个任务

5. thenCompose/thenComposeAsync

  • 在一个任务执行完成后,将该任务的执行结果作为入参,传到指定的方法去执行,该方法会返回一个新的CompletableFuture实例。
package com.example.mavendemo;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

public class Test12 {

    static ExecutorService fishExecutor = Executors.newCachedThreadPool((r) -> new Thread(r, "fish"));
    static UserService userService = new UserService();

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> first =
            CompletableFuture.supplyAsync(() -> userService.number("996"), fishExecutor);
        CompletableFuture<String> thenCompose = first.thenCompose(
            s -> CompletableFuture.supplyAsync(() -> userService.compute("这是第二个任务,接收到的入参是:" + s), fishExecutor));
        System.out.println(thenCompose.join());
        fishExecutor.shutdown();
    }

    @Slf4j
    static class UserService {

        public Integer number(String number) {
            log.info(number);
            return Integer.valueOf(number);
        }

        public String compute(String message) {
            log.info(message);
            return message;
        }

    }
}
  • 打印
13:57:16.096 [fish] INFO com.example.mavendemo.Test12$UserService - 996
13:57:16.098 [fish] INFO com.example.mavendemo.Test12$UserService - 这是第二个任务,接收到的入参是:996
这是第二个任务,接收到的入参是:996

注意点

  1. 如果任务执行发生异常,需要调用get()或join()获取返回值,才能获取异常信息;或者使用try…catch…或者使用exceptionally方法。

  2. CompletableFuture的get()方法是阻塞的,如果使用它来获取异步调用的返回值,最好添加超时时间,例如get(10, TimeUnit.SECONDS)

  3. 推荐使用自定义线程池,这样可以根据需要优化线程池配置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值