Java8新特性 之CompletableFuture

谁都会走 提交于 2020-03-02 10:20:37

参考

Java CompletableFuture 详解
CompletableFuture基本用法
使用了CompletableFuture之后,程序性能提升了三倍
Java8的CompletableFuture进阶之道–开发时看这个

CompletableFuture引入

Future是JDK5添加的类,用来描述一个异步计算的结果。可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,也可以使用cancel方法停止任务的执行。

public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    Future<Integer> f = executorService.submit(() ->{
        // 长时间的异步计算
        // 然后返回结果
        System.out.println("A");
        return 100;
    });
//        while(!f.isDone()){
//            System.out.println("D");
//        }
    System.out.println("B");
    Integer i = f.get();
    System.out.println("C" + i);
    executorService.shutdown();
    return;
}

结果:
B
A
C100

虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,所以可以用观察者设计模式,当计算结果完成及时通知监听者。

在JDK8里面,就新增了CompletableFuture类,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

CompletableFuture实现了Future、CompletionStage接口。CompletionStage接口定义了可与其他Future组合成异步计算契约。

使用CompletableFuture作为Future实现

CompletableFuture类实现Future接口,因此可以将其用作Future实现,但需要额外的完成实现逻辑。

例如:可以使用无构参构造函数创建此类的实例,然后使用complete方法完成。消费者可以使用get方法来阻塞当前线程,直到get()结果。
在下面的示例中,有一个创建CompletableFuture实例的方法,然后在另一个线程中计算并立即返回Future。
计算完成后,该方法通过将结果提供给完整方法来完成Future:

public static void main(String[] args) throws Exception{
    Future<String> future = calculateAsync();
    System.out.println(Thread.currentThread().getName() + " main");
    System.out.println(future.get());
}

public static Future<String> calculateAsync() {
    CompletableFuture<String> completableFuture = new CompletableFuture<>();
    Executors.newCachedThreadPool().execute(() -> {
        System.out.println(Thread.currentThread().getName() + " execute");
        String result = "Hello " + " World";
        completableFuture.complete(result);
    });
    return completableFuture;
}

结果:
main main
pool-1-thread-1 execute
Hello  World

每一种方法都有三种形式

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
  • 非异步方法由当前线程或调用线程执行
  • 不带executor的异步方法使用asyncPool来执行
    1)如果不支持多线程,则新建一个线程专门执行
    2)否则使用ForkJoinPool.commonPool()执行
  • 另一种异步方法使用executor执行

创建一个异步任务

CompletableFuture.completedFuture是一个静态辅助方法,用来返回一个已经计算好的CompletableFuture

public static <U> CompletableFuture<U> completedFuture(U value)

Async结尾的方法都是可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,默认会在ForkJoinPool.commonPool()中执行。

  • runAsync方法:它以Runnabel函数式接口类型为参数,所以CompletableFuture的计算结果为空。
  • supplyAsync方法:以Supplier<U>函数式接口类型为参数,所以CompletableFuture有返回值,且计算结果类型为U。
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> System.out.println("runAsync"));
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "supplyAsync");

System.out.println(future1.get());
System.out.println(future2.get());

结果:
runAsync
null
supplyAsync

计算完成时对结果的处理 whenComplete、exceptionally、handle

当CompletableFuture的计算结果完成,或者抛出异常的时候,我们可以执行特定的Action。

  • 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)

参数类型为 BiConsumer<? super T, ? super Throwable> 会获取上一步计算的计算结果和异常信息。
以Async结尾的方法可能会使用其它的线程去执行,如果使用相同的线程池,也可能会被同一个线程选中执行。

public class ThreadUtil {
    public static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
}

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(100);
    return 20;
}).whenCompleteAsync((v, e) -> {
    System.out.println("VV:" + v);
    System.out.println("EE:" + e);
});
System.out.println("A");
System.out.println(future.get());
System.out.println("B");

结果:
A
VV:20
EE:null
20
B
  • public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)

该方法是对异常情况的处理,当函数异常时执行。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(100);
    return 20/0;
}).whenCompleteAsync((v, e) -> {
    System.out.println("VV:" + v);
    System.out.println("EE:" + e);
}).exceptionally((e)->{
    System.out.println(e.getMessage());
    return 30;
});
System.out.println("A");
System.out.println(future.get());
System.out.println("B");

结果:
A
VV:null
EE:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
30
B
  • 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)

handle 方法和whenComplete方法类似,只不过接收的是一个 BiFunction<? super T,Throwable,? extends U> fn 类型的参数,因此有 whenComplete 方法和 转换的功能 (thenApply)。
以Async结尾的方法可能会使用其它的线程去执行,如果使用相同的线程池,也可能会被同一个线程选中执行。

CompletableFuture<Integer> future = CompletableFuture
        .supplyAsync(() -> {
            ThreadUtil.sleep(100);
            return 20;
        })
        .handle((t, e)->{
            System.out.println("TT:" + t);
            System.out.println("EE:" + e);
            return 10;
        });
System.out.println("A");
System.out.println(future.get());
System.out.println("B");
结果:
A
TT:20
EE:null
10
B
// 异常情况
CompletableFuture<Integer> future = CompletableFuture
        .supplyAsync(() -> {
            ThreadUtil.sleep(100);
            return 20/0;
        })
        .handle((t, e)->{
            System.out.println("TT:" + t);
            System.out.println("EE:" + e);
            return 10;
        });
System.out.println("A");
System.out.println(future.get());
System.out.println("B");
结果:
A
TT:null
EE:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
10
B

结果处理转换 thenApply

当前阶段正常完成以后执行,而且当前阶段的执行的结果会作为下一阶段的输入参数。
thenApplyAsync默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。
thenApply相当于回调函数(callback)。

CompletableFuture 由于有回调,可以不必等待一个计算完成而阻塞着调用线程,可以在一个结果计算完成之后紧接着执行某个Action。我们可以将这些操作串联起来。

  • public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
  • public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
  • public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
CompletableFuture<Integer> future = CompletableFuture
        .supplyAsync(() -> {
            ThreadUtil.sleep(100);
            return 20;
        })
        .thenApply((f1)->{
            System.out.println("F1:" + f1);
            return f1 * 2;
        })
        .thenApply((f2)->{
            System.out.println("F2:" + f2);
            return f2 * 2;
        })
        .thenApply((f3)->{
            System.out.println("F3:" + f3);
            return f3 * 2;
        });
System.out.println("A");
System.out.println("future:" + future.get());
System.out.println("B");

结果:
A
F1:20
F2:40
F3:80
future:160
B

这些方法不是马上执行的,也不会阻塞,而是前一个执行完成后继续执行下一个。
和 handle 方法的区别是,handle 会处理正常计算值和异常,不会抛出异常。而 thenApply 只会处理正常计算值,有异常则抛出。

纯消费 thenAccept、thenRun、thenAcceptBoth、runAfterBoth

thenAccept和thenRun都是无返回值的。如果说thenApply是不停的输入输出的进行生产,那么thenAccept和thenRun就是在进行消耗。它们是整个计算的最后两个阶段。

  • thenAccept接收上一阶段的输出作为本阶段的输入
  • thenRun根本不关心前一阶段的输出,根本不不关心前一阶段的计算结果,因为它不需要输入参数

单纯的去消费结果而不会返回新的值,因些计算结果为 Void。

  • public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
  • public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
  • public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(100);
    return 20;
}).thenAccept((c)->{
    System.out.println("CC:" + c);
}).thenAcceptAsync((c2)->{
    System.out.println("C2:" + c2);
});
System.out.println("A");
System.out.println(future.get());
System.out.println("B");

结果:
A
CC:20
C2:null
null
B
  • 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)
    和 thenAccept 相比,参数类型多了一个 CompletionStage<? extends U> other,BiConsumer第一个参数接收第一个supplyAsync返回值,第二个参数接收CompletionStage返回值。
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName());
    ThreadUtil.sleep(100);
    return 20;
}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName());
    return 2;
}), (a, b)->{
    System.out.println(Thread.currentThread().getName() + "-AA:" + a);
    System.out.println(Thread.currentThread().getName() + "-BB:" + b);
});
System.out.println("A");
System.out.println(future.get());
System.out.println("B");

结果:
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
A
ForkJoinPool.commonPool-worker-1-AA:20
ForkJoinPool.commonPool-worker-1-BB:2
null
B
  • 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)
    runAfterBoth 和以上方法不同,传一个 Runnable 类型的参数,不接收上一级的返回值
    更彻底的:
  • public CompletableFuture<Void> thenRun(Runnable action)
  • public CompletableFuture<Void> thenRunAsync(Runnable action)
  • public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)

以上是彻底的纯消费,完全忽略计算结果。

组合 thenCompose、thenCombine

  • 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)

以上接收类型为 Function<? super T,? extends CompletionStage<U>> fn ,fn 接收上一级返回的结果,并返回一个新的 CompletableFuture。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + " A");
    ThreadUtil.sleep(100);
    return 20;
}).thenApply((f)->{
    System.out.println(Thread.currentThread().getName() + " B");
    return f + 10;
}).thenCompose((s)->{
    System.out.println(Thread.currentThread().getName() + " SS:" + s);
    return CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName() + " C");
        return s * 5;
    });
});
System.out.println("A");
System.out.println(future.get());
System.out.println("B");

结果:
ForkJoinPool.commonPool-worker-1 A
A
ForkJoinPool.commonPool-worker-1 B
ForkJoinPool.commonPool-worker-1 SS:30
ForkJoinPool.commonPool-worker-2 C
150
B
  • 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)

两个CompletionStage是并行执行的,它们之间并没有先后依赖顺序,other并不会等待先前的CompletableFuture执行完毕后再执行。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(1000);
    System.out.println(Thread.currentThread().getName());
    return 20;
}).thenApply((f)->{
    ThreadUtil.sleep(1000);
    System.out.println(Thread.currentThread().getName());
    return f + 10;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + " thenCombineAsync");
    return 10;
}), (x, y)->{
    System.out.println(Thread.currentThread().getName() + " XX:" + x);
    System.out.println(Thread.currentThread().getName() + " YY:" + y);
    return x + y;
});
System.out.println("A");
System.out.println(future.get());
System.out.println("B");

结果:
ForkJoinPool.commonPool-worker-2 thenCombineAsync
A
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1 XX:30
ForkJoinPool.commonPool-worker-1 YY:10
40
B

thenCombinesupplyAsync 不一定哪个先哪个后,是并行执行的。

任意一个方法执行完成就结束acceptEither、applyToEither

  • 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)
Random random = new Random();
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(random.nextInt(1000));
    return "A";
}).acceptEither(CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(random.nextInt(1000));
    return "B";
}), (c) -> {
    System.out.println(c);
});
System.out.println(future.get());

结果:
B
null
或者:
A
null

以上代码有时输出A,有时输出B,哪个Future先执行完就会根据它的结果计算。
acceptEither方法是当任意一个 CompletionStage 完成的时候,action 这个消费者就会被执行。这个方法返回 CompletableFuture<Void>

  • 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)

applyToEither 方法是当任意一个 CompletionStage 完成的时候,fn会被执行,它的返回值会当作新的CompletableFuture<U>的计算结果。

Random random = new Random();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(random.nextInt(1000));
    return "A";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
    ThreadUtil.sleep(random.nextInt(1000));
    return "B";
}), (c) -> {
    System.out.println(c);
    return c + "DD";
});
System.out.println(future.get());

结果:
B
BDD
或者:
A
ADD

acceptEither 和 applyToEither 区别是: acceptEither 没有返回值,applyToEither 有返回值。

辅助方法allOf、anyOf

  • public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
    当所有的CompletableFuture都执行完后才往下执行,没有返回值。
  • public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
    当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同,返回一个Object类型的值。

使用CompletableFuture提升程序性能

如果每个操作都很简单的话,没有必要用这种多线程异步的方式,因为创建线程还需要时间,还不如直接同步执行来得快。

只有当每个操作很复杂需要花费相对很长的时间(比如,调用多个其它的系统的接口;比如,商品详情页面这种需要从多个系统中查数据显示的)的时候用CompletableFuture才合适,不然区别真的不大,还不如顺序同步执行。

定义一个对象:

@Data
public class UserInfo {
    private Integer id;
    private String name;
    private Integer jobId;
    private String jobDes;
    private Integer carId;
    private String carDes;
    private Integer homeId;
    private String homeDes;
}

这个对象里面的homeid,jobid,carid都是用于匹配对应的住房信息描述,职业信息描述,购车信息描述。
对于将id转换为描述信息的方式需要通过额外的sql查询,这里做了个简单的工具类来进行模拟:

import java.util.concurrent.TimeUnit;

public class QueryUtils {

    public String queryCar(Integer carId){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "car_desc";
    }
    public String queryJob(Integer jobId){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "job_desc";
    }
    public String queryHome(Integer homeId){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "home_desc";
    }
}

假设每次查询需要消耗1s,那么遍历的一个size为n的集合查询消耗的时间就是n * 3s。

下面来使用CompletableFuture提升性能:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class QueryUserService {

    private Supplier<QueryUtils> queryUtilsSupplier = new Supplier<QueryUtils>() {
        @Override
        public QueryUtils get() {
            return new QueryUtils();
        }
    };

    // 传统方式
    public UserInfo converUserInfo(UserInfo userInfo) {
        userInfo.setCarDes(queryUtilsSupplier.get().queryCar(userInfo.getCarId()));
        userInfo.setHomeDes(queryUtilsSupplier.get().queryHome(userInfo.getHomeId()));
        userInfo.setJobDes(queryUtilsSupplier.get().queryJob(userInfo.getJobId()));
        return userInfo;
    }

    // CompletableFuture方式
    public UserInfo converUserInfoForFuture(UserInfo userInfo) {
        CompletableFuture<String> getCarDesc = CompletableFuture.supplyAsync(() -> queryUtilsSupplier.get().queryCar(userInfo.getCarId()));
        getCarDesc.thenAccept((carDesc) -> userInfo.setCarDes(carDesc));

        CompletableFuture<String> getHomeDesc = CompletableFuture.supplyAsync(() -> queryUtilsSupplier.get().queryHome(userInfo.getHomeId()));
        getHomeDesc.thenAccept((homeDesc) -> userInfo.setHomeDes(homeDesc));

        CompletableFuture<String> getJobDesc = CompletableFuture.supplyAsync(() -> queryUtilsSupplier.get().queryJob(userInfo.getJobId()));
        getJobDesc.thenAccept((jobDesc) -> userInfo.setJobDes(jobDesc));

        CompletableFuture<Void> getUserInfo = CompletableFuture.allOf(getCarDesc, getHomeDesc, getJobDesc);
        getUserInfo.thenAccept(new Consumer<Void>() {
            @Override
            public void accept(Void result) {
                System.out.println("全部完成查询");
            }
        });
        getUserInfo.join();
        return userInfo;
    }
    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        // 多线程环境需要注意线程安全问题
        List<UserInfo> userInfoList = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i <= 20; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(i);
            userInfo.setName("username" + i);
            userInfo.setCarId(i);
            userInfo.setJobId(i);
            userInfo.setHomeId(i);
            userInfoList.add(userInfo);
        }
        QueryUserService queryUserService = new QueryUserService();
        //stream 查询一个用户花费3s  并行计算后一个用户1秒左右 查询21个用户花费21秒
        userInfoList.stream().map(userInfo -> {
            userInfo = queryUserService.converUserInfoForFuture(userInfo);
            return userInfo;
        }).collect(Collectors.toList());
        System.out.println("=============");
        long end = System.currentTimeMillis();
        System.out.println(end - begin);
    }
}

CompletableFuture使用详解

static方法说明

CompletableFuture的几个 static 方法,它们可以实例化一个 CompletableFuture 实例。

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
  • runAsync 方法接收的是 Runnable 的实例,但是它没有返回值。
  • supplyAsync 方法是JDK8函数式接口,无参数,会返回一个结果。
  • 这两个方法是 executor 的升级,表示让任务在指定的线程池中执行,不指定的话,通常任务是在 ForkJoinPool.commonPool() 线程池中执行的。

supplyAsync()使用

静态方法runAsyncsupplyAsync允许我们相应地从RunnableSupplier功能类型中创建CompletableFuture实例。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + " A");
    return "Hello";
});
System.out.println(Thread.currentThread().getName() + " B");
System.out.println(future.get());

结果:
main B
ForkJoinPool.commonPool-worker-1 A
Hello

thenRun()使用

在两个任务任务A,任务B中,如果既不需要任务A的值也不想在任务B中引用,那么你可以将Runnable lambda 传递给thenRun()方法。
在下面的示例中,在调用future.get()方法之后,我们只需在控制台中打印一行:
模板

CompletableFuture.runAsync(() -> {}).thenRun(() -> {}); 
CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {});
  • 第一行用的是 thenRun(Runnable runnable),任务 A 执行完执行 B,并且 B 不需要 A 的结果。
  • 第二行用的是 supplyAsync(Supplier<U> supplier),任务 A 执行完执行 B,会返回resultA,但是 B 不需要 A 的结果。

实战

CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + " A");
    return "Hello";
}).thenRun(() -> {
    System.out.println(Thread.currentThread().getName() + " Computation finished.");
});
System.out.println(Thread.currentThread().getName() + " B");
System.out.println(completableFuture.get());

结果:
ForkJoinPool.commonPool-worker-1 A
main Computation finished.
main B
null

thenAccept()使用

在两个任务任务A,任务B中,如果你不需要在Future中有返回值,则可以用 thenAccept方法接收将计算结果传递给它。最后的future.get()调用返回Void类型的实例。
模板

CompletableFuture.runAsync(() -> {}).thenAccept(resultA -> {}); 
CompletableFuture.supplyAsync(() -> "resultA").thenAccept(resultA -> {});
  • 第一行中,runAsync不会有返回值,第二个方法thenAccept,接收到的resultA值为null,同时任务B也不会有返回结果。
  • 第二行中,supplyAsync有返回值,同时任务B不会有返回结果。
    实战
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
    System.out.println(Thread.currentThread().getName() + " A");
    return "Hello";
});
CompletableFuture<Void> future = completableFuture.thenAccept(s -> {
    System.out.println(Thread.currentThread().getName() + " Computation returned: " + s);
});
System.out.println(Thread.currentThread().getName() + " B");
System.out.println(future.get());

结果:
ForkJoinPool.commonPool-worker-1 A
main Computation returned: Hello
main B
null
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!