CompletableFuture

转载自:https://www.jianshu.com/p/dff9063e1ab6

一、Future

JDK 5引入了Future模式。Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算。

Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。

Future的接口很简单,只有五个方法。

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future 接口的方法介绍如下:

  • boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
  • boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
  • boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回 true
  • V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得 V 类型的结果。InterruptedException 线程被中断异常, ExecutionException 任务执行异常,如果任务被取消,还会抛出 CancellationException
  • V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的 get 功能一样,多了设置超时时间。参数 timeout 指定超时时间,uint 指定时间的单位,在枚举类 TimeUnit 中有相关的定义。如果计 算超时,将抛出 TimeoutException

一般情况下,我们会结合 Callable 和 Future 一起使用,通过 ExecutorService 的 submit 方法执行 Callable,并返回 Future。

ExecutorService executor = Executors.newCachedThreadPool();

        Future<String> future = executor.submit(() -> { //Lambda 是一个 callable, 提交后便立即执行,这里返回的是 FutureTask 实例
            System.out.println("running task");
            Thread.sleep(10000);
            return "return task";
        });

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }

        System.out.println("do something else");  //前面的的 Callable 在其他线程中运行着,可以做一些其他的事情

        try {
            System.out.println(future.get());  //等待 future 的执行结果,执行完毕之后打印出来
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {

        } finally {
            executor.shutdown();
        }

比起 future.get(),其实更推荐使用 get (long timeout, TimeUnit unit) 方法,设置了超时时间可以防止程序无限制的等待 future 的结果。

二、CompletableFuture 介绍

2.1 Future 模式的缺点

  • Future 虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知 Future 什么时候完成。
  • 要么使用阻塞,在 future.get() 的地方等待 future 返回的结果,这时又变成同步操作。要么使用 isDone() 轮询地判断 Future 是否完成,这样会耗费 CPU 的资源。

2.2 CompletableFuture 介绍

Netty、Guava 分别扩展了 Java 的 Future 接口,方便异步编程。

Java 8 新增的 CompletableFuture 类正是吸收了所有 Google Guava 中 ListenableFuture 和 SettableFuture 的特征,还提供了其它强大的功能,让 Java 拥有了完整的非阻塞编程模型:Future、Promise 和 Callback(在 Java8 之前,只有无 Callback 的 Future)。

CompletableFuture 能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。

CompletableFuture 弥补了 Future 模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过 thenAccept、thenApply、thenCompose 等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。

三、 CompletableFuture 特性

3.1 CompletableFuture 的静态工厂方法

方法名 描述
runAsync(Runnable runnable) 使用 ForkJoinPool.commonPool() 作为它的线程池执行异步代码。
runAsync(Runnable runnable, Executor executor) 使用指定的 thread pool 执行异步代码。
supplyAsync(Supplier supplier) 使用 ForkJoinPool.commonPool() 作为它的线程池执行异步代码,异步操作有返回值
supplyAsync(Supplier supplier, Executor executor) 使用指定的 thread pool 执行异步代码,异步操作有返回值

runAsync 和 supplyAsync 方法的区别是 runAsync 返回的 CompletableFuture 是没有返回值的。

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println("Hello");
        });

try {
  future.get();
} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
}

System.out.println("CompletableFuture");

而 supplyAsync 返回的 CompletableFuture 是由返回值的,下面的代码打印了 future 的返回值。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

try {
  System.out.println(future.get());
} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
}

System.out.println("CompletableFuture");

3.2 Completable

方法名 描述
complete(T t) 完成异步执行,并返回 future 的结果
completeExceptionally(Throwable ex) 异步执行不正常的结束

future.get() 在等待执行结果时,程序会一直 block,如果此时调用 complete(T t) 会立即执行。

CompletableFuture<String> future  = CompletableFuture.supplyAsync(() -> "Hello");

future.complete("World");

try {
  System.out.println(future.get());
} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
}

执行结果:

World

可以看到 future 调用 complete(T t) 会立即执行。但是 complete(T t) 只能调用一次,后续的重复调用会失效。

如果 future 已经执行完毕能够返回结果,此时再调用 complete(T t) 则会无效。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

try {
  Thread.sleep(5000);
} catch (InterruptedException e) {
  e.printStackTrace();
}

future.complete("World");

try {
  System.out.println(future.get());
} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
}

执行结果:

Hello

如果使用 completeExceptionally(Throwable ex) 则抛出一个异常,而不是一个成功的结果。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

future.completeExceptionally(new Exception());

try {
  System.out.println(future.get());
} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
}

执行结果:

java.util.concurrent.ExecutionException: java.lang.Exception
...

3.3 转换

我们可以通过CompletableFuture来异步获取一组数据,并对数据进行一些转换,类似RxJava、Scala的map、flatMap操作。

3.3.1 map

方法名 描述
thenApply(Function<? super T,? extends U> fn) 接受一个 Function<? super T,? extends U> 参数用来转换 CompletableFuture
thenApplyAsync(Function<? super T,? extends U> fn) 接受一个 Function<? super T,? extends U> 参数用来转换 CompletableFuture,使用 ForkJoinPool
thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) 接受一个 Function<? super T,? extends U> 参数用来转换 CompletableFuture,使用指定的线程池

thenApply 的功能相当于将 CompletableFuture<T> 转换成 CompletableFuture<U>

 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
        future = future.thenApply(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s + " World";
            }
        }).thenApply(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        });
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

再用 lambda 表达式简化一下

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
                .thenApply(s -> s + " World").thenApply(String::toUpperCase);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

HELLO WORLD

下面的例子,展示了数据流的类型经历了如下的转换:String -> Integer -> Double。

CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> "10")
                .thenApply(Integer::parseInt)
                .thenApply(i->i*10.0);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

100.0

3.3.2 flatMap

方法名 描述
thenCompose(Function<? super T, ? extends CompletionStage> fn) 在异步操作完成的时候对异步操作的结果进行一些操作,并且仍然返回 CompletableFuture 类型。
thenComposeAsync(Function<? super T, ? extends CompletionStage> fn) 在异步操作完成的时候对异步操作的结果进行一些操作,并且仍然返回 CompletableFuture 类型。使用 ForkJoinPool。
thenComposeAsync(Function<? super T, ? extends CompletionStage> fn,Executor executor) 在异步操作完成的时候对异步操作的结果进行一些操作,并且仍然返回 CompletableFuture 类型。使用指定的线程池。

thenCompose 可以用于组合多个 CompletableFuture,将前一个结果作为下一个计算的参数,它们之间存在着先后顺序。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
                .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

Hello World

下面的例子展示了多次调用 thenCompose()

CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> "100")
                .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "100"))
                .thenCompose(s -> CompletableFuture.supplyAsync(() -> Double.parseDouble(s)));

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

100100.0

3.4 组合

方法名 描述
thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) 当两个 CompletableFuture 都正常完成后,执行提供的 fn,用它来组合另外一个 CompletableFuture 的结果。
thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) 当两个 CompletableFuture 都正常完成后,执行提供的 fn,用它来组合另外一个 CompletableFuture 的结果。使用 ForkJoinPool。
thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor) 当两个 CompletableFuture 都正常完成后,执行提供的 fn,用它来组合另外一个 CompletableFuture 的结果。使用指定的线程池。

现在有 CompletableFuture<T>CompletableFuture<U> 和一个函数 (T,U)->V,thenCombine 就是将 CompletableFuture<T>CompletableFuture<U> 变为 CompletableFuture<V>

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "100");
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 100);

        CompletableFuture<Double> future = future1.thenCombine(future2, (s, i) -> Double.parseDouble(s + i));

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

100100.0

使用 thenCombine() 之后 future1、future2 之间是并行执行的,最后再将结果汇总。这一点跟 thenCompose() 不同。

thenAcceptBoth 跟 thenCombine 类似,但是返回 CompletableFuture<Void> 类型。

方法名 描述
thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action) 当两个 CompletableFuture 都正常完成后,执行提供的 action,用它来组合另外一个 CompletableFuture 的结果。
thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action) 当两个 CompletableFuture 都正常完成后,执行提供的 action,用它来组合另外一个 CompletableFuture 的结果。使用 ForkJoinPool。
thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action, Executor executor) 当两个 CompletableFuture 都正常完成后,执行提供的 action,用它来组合另外一个 CompletableFuture 的结果。使用指定的线程池。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "100");
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 100);

        CompletableFuture<Void> future = future1.thenAcceptBoth(future2, (s, i) -> System.out.println(Double.parseDouble(s + i)));// 区别1

        try {
            future.get();// 区别2
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

100100.0

3.5 计算结果完成时的处理

当 CompletableFuture 完成计算结果后,我们可能需要对结果进行一些处理。

3.5.1 执行特定的 Action

方法名 描述
whenComplete(BiConsumer<? super T,? super Throwable> action) 当 CompletableFuture 完成计算结果时对结果进行处理,或者当 CompletableFuture 产生异常的时候对异常进行处理。
whenCompleteAsync(BiConsumer<? super T,? super Throwable> action) 当 CompletableFuture 完成计算结果时对结果进行处理,或者当 CompletableFuture 产生异常的时候对异常进行处理。使用 ForkJoinPool。
whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor) 当 CompletableFuture 完成计算结果时对结果进行处理,或者当 CompletableFuture 产生异常的时候对异常进行处理。使用指定的线程池。
CompletableFuture.supplyAsync(() -> "Hello")
                .thenApply(s->s+" World")
                .thenApply(s->s+ "\nThis is CompletableFuture demo")
                .thenApply(String::toLowerCase)
                .whenComplete((result, throwable) -> System.out.println(result));

执行结果:

hello world
this is completablefuture demo

3.5.2 执行完 Action 可以做转换

方法名 描述
handle(BiFunction<? super T, Throwable, ? extends U> fn) 当 CompletableFuture 完成计算结果或者抛出异常的时候,执行提供的 fn
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) 当 CompletableFuture 完成计算结果或者抛出异常的时候,执行提供的 fn,使用 ForkJoinPool。
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) 当 CompletableFuture 完成计算结果或者抛出异常的时候,执行提供的 fn,使用指定的线程池。
CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> "100")
                .thenApply(s->s+"100")
                .handle((s, t) -> s != null ? Double.parseDouble(s) : 0);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:

100100.0

在这里,handle() 的参数是 BiFunction,apply() 方法返回 R,相当于转换的操作。

@FunctionalInterface
public interface BiFunction<T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     */
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

而 whenComplete() 的参数是 BiConsumer,accept() 方法返回 void。

@FunctionalInterface
public interface BiConsumer<T, U> {

    /**
     * Performs this operation on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     */
    void accept(T t, U u);

    /**
     * Returns a composed {@code BiConsumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code BiConsumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}

所以,handle() 相当于 whenComplete()+ 转换。

3.5.3 纯消费 (执行 Action)

方法名 描述
thenAccept(Consumer<? super T> action) 当 CompletableFuture 完成计算结果,只对结果执行 Action,而不返回新的计算值
thenAcceptAsync(Consumer<? super T> action) 当 CompletableFuture 完成计算结果,只对结果执行 Action,而不返回新的计算值,使用 ForkJoinPool。
thenAcceptAsync(Consumer<? super T> action, Executor executor) 当 CompletableFuture 完成计算结果,只对结果执行 Action,而不返回新的计算值

thenAccept() 是只会对计算结果进行消费而不会返回任何结果的方法。

CompletableFuture.supplyAsync(() -> "Hello")
                .thenApply(s->s+" World")
                .thenApply(s->s+ "\nThis is CompletableFuture demo")
                .thenApply(String::toLowerCase)
                .thenAccept(System.out::print);

执行结果:

hello world
this is completablefuture demo

3.6 Either

Either 表示的是两个 CompletableFuture,当其中任意一个 CompletableFuture 计算完成的时候就会执行。

方法名 描述
acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) 当任意一个 CompletableFuture 完成的时候,action 这个消费者就会被执行。
acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) 当任意一个 CompletableFuture 完成的时候,action 这个消费者就会被执行。使用 ForkJoinPool
acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) 当任意一个 CompletableFuture 完成的时候,action 这个消费者就会被执行。使用指定的线程池
Random random = new Random();

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future2";
        });

        CompletableFuture<Void> future =  future1.acceptEither(future2,str->System.out.println("The future is "+str));

        try {
            future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果:The future is from future1 或者 The future is from future2。
因为 future1 和 future2,执行的顺序是随机的。

applyToEither 跟 acceptEither 类似。

方法名 描述
applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn) 当任意一个 CompletableFuture 完成的时候,fn 会被执行,它的返回值会当作新的 CompletableFuture 的计算结果。
applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn) 当任意一个 CompletableFuture 完成的时候,fn 会被执行,它的返回值会当作新的 CompletableFuture 的计算结果。使用 ForkJoinPool
applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor) 当任意一个 CompletableFuture 完成的时候,fn 会被执行,它的返回值会当作新的 CompletableFuture 的计算结果。使用指定的线程池
Random random = new Random();

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future2";
        });

        CompletableFuture<String> future =  future1.applyToEither(future2,str->"The future is "+str);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

执行结果也跟上面的程序类似。

3.7 其他方法

allOf、anyOf 是 CompletableFuture 的静态方法。

3.7.1 allOf

方法名 描述
allOf(CompletableFuture<?>... cfs) 在所有 Future 对象完成后结束,并返回一个 future。

allOf() 方法所返回的 CompletableFuture,并不能组合前面多个 CompletableFuture 的计算结果。于是我们借助 Java 8 的 Stream 来组合多个 future 的结果。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "tony");

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "cafei");

        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "aaron");

        CompletableFuture.allOf(future1, future2, future3)
                .thenApply(v ->
                Stream.of(future1, future2, future3)
                        .map(CompletableFuture::join)
                        .collect(Collectors.joining(" ")))
                .thenAccept(System.out::print);

执行结果:

tony cafei aaron

3.7.2 anyOf

方法名 描述
anyOf(CompletableFuture<?>... cfs) 在任何一个 Future 对象结束后结束,并返回一个 future。
Random rand = new Random();
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "from future1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "from future2";
        });
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "from future3";
        });

        CompletableFuture<Object> future =  CompletableFuture.anyOf(future1,future2,future3);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

使用 anyOf() 时,只要某一个 future 完成,就结束了。所以执行结果可能是 "from future1"、"from future2"、"from future3" 中的任意一个。

anyOf 和 acceptEither、applyToEither 的区别在于,后两者只能使用在两个 future 中,而 anyOf 可以使用在多个 future 中。

3.8 CompletableFuture 异常处理

CompletableFuture 在运行时如果遇到异常,可以使用 get() 并抛出异常进行处理,但这并不是一个最好的方法。CompletableFuture 本身也提供了几种方式来处理异常。

3.8.1 exceptionally

方法名 描述
exceptionally(Function<Throwable,? extends T> fn) 只有当 CompletableFuture 抛出异常的时候,才会触发这个 exceptionally 的计算,调用 function 计算值。
CompletableFuture.supplyAsync(() -> "hello world")
                .thenApply(s -> {
                    s = null;
                    int length = s.length();
                    return length;
                }).thenAccept(i -> System.out.println(i))
                .exceptionally(t -> {
                    System.out.println("Unexpected error:" + t);
                    return null;
                });

执行结果:

Unexpected error:java.util.concurrent.CompletionException: java.lang.NullPointerException

对上面的代码稍微做了一下修改,修复了空指针的异常。

CompletableFuture.supplyAsync(() -> "hello world")
                .thenApply(s -> {
//                    s = null;
                    int length = s.length();
                    return length;
                }).thenAccept(i -> System.out.println(i))
                .exceptionally(t -> {
                    System.out.println("Unexpected error:" + t);
                    return null;
                });

执行结果:


3.8.2 whenComplete

whenComplete 在上文其实已经介绍过了,在这里跟 exceptionally 的作用差不多,可以捕获任意阶段的异常。如果没有异常的话,就执行 action。

CompletableFuture.supplyAsync(() -> "hello world")
                .thenApply(s -> {
                    s = null;
                    int length = s.length();
                    return length;
                }).thenAccept(i -> System.out.println(i))
                .whenComplete((result, throwable) -> {

                    if (throwable != null) {
                       System.out.println("Unexpected error:"+throwable);
                    } else {
                        System.out.println(result);
                    }

                });

执行结果:

Unexpected error:java.util.concurrent.CompletionException: java.lang.NullPointerException

跟 whenComplete 相似的方法是 handle,handle 的用法在上文中也已经介绍过。

四. CompletableFuture VS Java8 Stream VS RxJava1 & RxJava2

CompletableFuture 有很多特性跟 RxJava 很像,所以将 CompletableFuture、Java 8 Stream 和 RxJava 做一个相互的比较。

composable lazy resuable async cached push back pressure
CompletableFuture 支持 不支持 支持 支持 支持 支持 不支持
Stream 支持 支持 不支持 不支持 不支持 不支持 不支持
Observable(RxJava1) 支持 支持 支持 支持 支持 支持 支持
Observable(RxJava2) 支持 支持 支持 支持 支持 支持 不支持
Flowable(RxJava2) 支持 支持 支持 支持 支持 支持 支持

五. 总结

Java 8 提供了一种函数风格的异步和事件驱动编程模型 CompletableFuture,它不会造成堵塞。CompletableFuture 背后依靠的是 fork/join 框架来启动新的线程实现异步与并发。当然,我们也能通过指定线程池来做这些事情。

CompletableFuture 特别是对微服务架构而言,会有很大的作为。举一个具体的场景,电商的商品页面可能会涉及到商品详情服务、商品评论服务、相关商品推荐服务等等。获取商品的信息时(/productdetails?productid=xxx),需要调用多个服务来处理这一个请求并返回结果。这里可能会涉及到并发编程,我们完全可以使用 Java 8 的 CompletableFuture 或者 RxJava 来实现。

posted @ 2021-12-02 15:36  liumeng哈哈哈  阅读(283)  评论(0编辑  收藏  举报