與CompletableFuture有關的一些知識

lpe234發表於2022-12-21
Java1.5引入了Future,在1.8中又引入了CompletableFuture。他的出現可以使我們更好的去對任務進行編排,合理的使用會極大的縮減多工的處理時間,達到事半功倍的目的。下面讓我們一塊來看一下與它相關的一些知識。

1 Java中的函數語言程式設計

在看CompletableFuture瞭解一點Java中函數語言程式設計相關的知識會更有用一些。

在Java1.8中引入了@FunctionalInterface,同時也在java.util.function包中引入了很多函式式介面。我們來看幾個常見的:

1.1 Consumer

Consumer消費者,關聯的方法為void accept(T t),有參無返回值。

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

1.2 Function

Function函式,關聯方法為R apply(T t),有參有返回值。

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

1.3 Predicate

Predicate謂語(斷言?),關聯方法為boolean test(T t),有參有返回值(bool)。

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

1.4 Supplier

Supplier供應者,關聯方法為T get(),無參有返回值。

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

2 CompletableFuture

後續的內容主要關注點會放在其內部各種API上面。在後續的方法中,因為CompletableFuture實現了CompletionStage使其具有鏈式呼叫能力。大多數方法都會類似如下:

  • xxx(Function func): 會上文的同一執行緒去執行當前任務
  • xxxAsync(Function func): 會啟用一個新執行緒去執行當前任務
  • xxxAsync(Function func, Executor executor): 會使用executor執行緒池中的新執行緒去執行當前任務

2.1 CompletableFuture的建立

  • supplyAsync: 以Supplier為參,有返回值。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
  • runAsync: 以Runnable為參,返回值為Void
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
  • completedFuture: 建立一個已有值且完成的CompletableFuture。
public static <U> CompletableFuture<U> completedFuture(U value)
  • failedFuture: 建立一個已出現異常且完成的CompletableFuture。
public static <U> CompletableFuture<U> failedFuture(Throwable ex)

可以看到runAsyncsupplyAsync都提供了額外引數Executor,在未指定executor時,預設會使用ForkJoinPool.commonPool()來執行非同步任務,而Parallel Stream預設情況下也會使用該執行緒池,共用的話可能會導致非核心業務搶佔核心業務的執行。一般來說都會使用自定義執行緒池來執行這些任務。

2.2 CompletableFuture的後續操作

2.2.1 執行一個任務(1)
  • apply: 有參有返回值。會將前面的返回值作為當前函式的輸入。
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)
  • accept: 有參無返回值。會將前面的輸出作為當前函式的輸入。
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)
  • run: 編排執行另外一個毫無相關的任務
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)

示例

@Test
void testApply() throws ExecutionException, InterruptedException {
    final CompletableFuture<Integer> future = CompletableFuture
            .supplyAsync(() -> 1)
            .thenApply((val) -> val + 1);
    assertEquals(2, future.get());
}

@Test
void testAccept() {
    final CompletableFuture<Void> future = CompletableFuture
            .supplyAsync(() -> 1)
            .thenAccept((val) -> {
                assertEquals(1, val);
            });
    future.join();
}

@Test
void testRun() {
    final CompletableFuture<Void> future = CompletableFuture
            .supplyAsync(() -> 1)
            .thenRun(() -> {});
    future.join();
}
2.2.1 執行一個任務(2)
  • whenComplete: 當任務完成時執行該方法,將返回值及異常傳入BiConsumer(有參無返回值)
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)
  • exceptionally: 當相應的任務出現異常時會呼叫該方法,將異常作為引數傳進來
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
public CompletableFuture<T> exceptionallyAsync(Function<Throwable, ? extends T> fn)
public CompletableFuture<T> exceptionallyAsync(Function<Throwable, ? extends T> fn, Executor executor)
  • handler: 接收處理前面任務的結果BiFunction(有參有返回值)
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)
2.2.2 執行兩個任務其中一個即可
  • applyToEither: 執行倆任務,返回其中一個未出現異常的,並將該輸出作為引數傳入後續Function(有參有返回值)
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)
  • acceptEither: 執行倆任務,返回其中一個未出現異常的,並將該輸出作為引數傳入後續Consumer(有參無返回值)
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)
  • runAfterEither: 執行倆任務,返回其中一個未出現異常的,並將該輸出作為引數傳入後續Runnable(無參無返回值)
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)
2.2.3 執行兩個任務全部完成
  • thenCombine: 執行兩個任務,並將這倆任務作為引數輸入BiFunction(有參有返回值)
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)
  • thenAcceptBoth: 執行兩個任務,並將這倆任務作為引數輸入BiConsumer(有參無返回值)
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)
  • runAfterBoth: 執行兩個任務,然後執行Runnable(無參無返回值)
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)
2.2.4 執行兩個有依賴的任務
  • thenCompose: 前一個任務的結果,要給第二個任務作為引數
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)
2.2.5 組合更多的任務
  • allOf: 等待所有任務都完成
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
  • anyOf: 等待完成任意一任務即可
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

2.3 結果獲取

2.3.1 join

任務完成時獲取資料,或者異常時丟擲異常。不過要注意的是,它丟擲的異常是(unchecked) exceptionCancellationExceptionCompletionException

public T join()
2.3.2 get

在獲取資料時: get()等待任務完成然後讀取結果; get(long timeout, TimeUnit unit)有限時間內等待完成讀取結果,或丟擲TimeoutException異常; getNow(T valueIfAbsent)立即獲取結果,或者預設值;

public T get() throws InterruptedException, ExecutionException
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 
public T getNow(T valueIfAbsent)

3 後記

今天主要熟悉了CompletableFuture的絕大部分API,只有在合適的場景去使用才能融會貫通。後面會結合實際場景來講講如何使用它來做任務編排。


echo '5Y6f5Yib5paH56ugOiDmjpjph5Eo5L2g5oCO5LmI5Zad5aW26Iy25ZWKWzkyMzI0NTQ5NzU1NTA4MF0pL+aAneWQpihscGUyMzQp' | base64 -d

相關文章