CompletableFuture 使用指南

FunTester發表於2024-06-17

在 Java 併發程式設計中,傳統的執行緒和同步機制如Thread類和Runnable介面提供了基本的並行執行能力,但它們的使用往往需要編寫大量的樣板程式碼來處理執行緒的建立、管理和同步,從而導致程式碼複雜且難以維護。為了解決這些問題,Java 5 引入了java.util.concurrent包,提供瞭如ExecutorServiceFuture等高階抽象來簡化併發程式設計。然而,Future介面在處理非同步任務時仍然存在一些侷限,例如無法方便地處理回撥、組合多個任務以及處理異常。

為了解決這些問題,Java 8 引入了CompletableFuture,它不僅實現了Future介面,還提供了豐富的 API 來支援非同步程式設計。透過CompletableFuture,開發者可以更優雅地處理非同步任務的執行、結果處理和異常處理。CompletableFuture提供了諸如thenApplythenAcceptthenCombine等方法,可以輕鬆地將多個非同步任務串聯或並行執行,並在任務完成後進行回撥處理。此外,CompletableFuture還支援自定義執行緒池,使得開發者可以靈活地管理執行緒資源,提高程式的併發效能和可維護性。

CompletableFuture的引入極大地簡化了 Java 併發程式設計,提供了一種更直觀、更強大的方式來編寫非同步和並行程式碼,使得複雜的併發任務變得更加易於實現和維護。

功能

CompletableFuture專注於非同步任務的結果,並提供豐富的 API 用於組合和錯誤處理。它負責:

  • 並行處理:可以將多個獨立的任務並行執行,然後合併結果。
  • 非同步回撥:可以在任務完成後執行回撥函式,而不阻塞主執行緒。
  • 異常處理:在非同步操作中更方便地處理異常情況。

程式碼示例

以下程式碼演示了在 Java 中使用來CompletableFuture處理非同步計算。

public static void main(String[] args) {  
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
        System.out.println("Hello,FunTester! " + Thread.currentThread().getName());  
        return "Hello,FunTester!";  
    });  
    future.thenAccept(System.out::println);  
    future.join();  
}

這個示例程式碼展示瞭如何使用 Java 的 CompletableFuture 類來非同步執行任務,並處理任務的結果。讓我們逐步解析一下:

  1. CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {...});這一行建立了一個 CompletableFuture 例項,並使用supplyAsync方法非同步執行提供的 lambda 表示式。lambda 表示式的程式碼塊中,首先列印了一個字串和當前執行緒名稱,然後返回字串"Hello,FunTester!"
  2. future.thenAccept(System.out::println);這一行註冊了一個回撥函式,當上一步非同步任務完成時,它會將任務的結果 (即字串"Hello,FunTester!"傳遞給System.out::println方法,從而將其列印到控制檯。
  3. future.join();這一行是一個阻塞操作,它會等待非同步任務完成。如果非同步任務已經完成,則立即返回;否則,它會一直等待直到非同步任務完成。

因此,執行這個程式時,它會先列印"Hello,FunTester! [執行緒名稱]"(這是在非同步任務中列印的),然後列印"Hello,FunTester!"(這是由thenAccept回撥列印的)。

這個示例展示了 CompletableFuture 如何簡化非同步程式設計。你可以使用 lambda 表示式來定義非同步任務,並使用thenAccept等方法來註冊對任務結果的處理邏輯。CompletableFuture 還提供了其他有用的方法,如thenApplythenCompose等,用於組合和鏈式執行多個非同步任務。

鏈式非同步任務

CompletableFuture的強大功能之一就是能夠將多個非同步任務連結在一起。處理複雜的非同步工作流時,這可以使程式碼更具可讀性和可維護性。

以下程式碼演示瞭如何CompletableFuture在 Java 中使用連結多個任務來建立一系列非同步計算。

import java.util.concurrent.CompletableFuture;

public class ChainingTasksExample {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> "Task 1")
            .thenApply(result -> result + " + Task 2")
            .thenApply(result -> result + " + Task 3")
            .thenAccept(System.out::println);
    }
}

這個案例中展示了 CompletableFuture 的鏈式呼叫和結果轉換的用法。讓我們逐步解析一下:

  1. CompletableFuture.supplyAsync(() -> "Task 1")
    • 這一行建立了一個 CompletableFuture 例項,並使用supplyAsync方法非同步執行一個 lambda 表示式,該表示式返回字串"Task 1"
  2. .thenApply(result -> result + " + Task 2")
    • thenApply方法接受一個函式式介面Function作為引數,該函式接收上一個任務的結果作為輸入,並返回一個新的結果。
    • 在這裡,lambda 表示式result -> result + " + Task 2"將上一個任務的結果 ("Task 1") 與字串" + Task 2"連線,返回"Task 1 + Task 2"
  3. .thenApply(result -> result + " + Task 3")
    • 這一行又使用thenApply方法,將上一個任務的結果 ("Task 1 + Task 2") 與字串" + Task 3"連線,返回"Task 1 + Task 2 + Task 3"
  4. .thenAccept(System.out::println);
    • thenAccept方法接受一個函式式介面Consumer作為引數,該介面消費上一個任務的結果,但不返回任何值。
    • 在這裡,使用System.out::println方法引用作為Consumer的實現,它將列印上一個任務的結果 ("Task 1 + Task 2 + Task 3")。

因此,當你執行這個程式碼時,它會非同步執行三個任務,每個任務在上一個任務的結果上追加一個字串。最終,它會將最終的結果"Task 1 + Task 2 + Task 3"列印到控制檯。

這個示例展示了 CompletableFuture 如何透過鏈式呼叫和結果轉換來組合多個非同步任務。每個thenApply方法都會在上一個任務完成後非同步執行,並將結果傳遞給下一個任務。最後,thenAccept方法用於消費最終的結果

錯誤處理

CompletableFuture提供了多種方法來處理非同步任務執行過程中發生的異常。您可以使用exceptionallyhandle和等方法whenComplete來妥善處理錯誤。

以下程式碼演示了在使用CompletableFutureJava 時如何正確處理錯誤。

import java.util.concurrent.CompletableFuture;

public class ErrorHandlingExample {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
            if (true) throw new RuntimeException("Something went wrong!");
            return "Success";
        }).exceptionally(ex -> {
            System.out.println("Error: " + ex.getMessage());
            return "Fallback result";
        }).thenAccept(System.out::println);
    }
}

超時管理

在非同步程式設計中,管理超時至關重要,以避免無限期地等待任務完成。提供和CompletableFuture等方法來有效地處理超時。

以下程式碼演示瞭如何CompletableFuture在 Java 中管理超時。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class TimeoutManagementExample {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        throw new IllegalStateException(e);
                    }
                    return "Result after delay";
                }).orTimeout(2, TimeUnit.SECONDS)
                .exceptionally(ex -> "Timeout occurred")
                .thenAccept(System.out::println);
    }
}

這個例子演示瞭如何使用CompletableFutureorTimeout方法來設定非同步任務的超時時間,以及如何在超時發生時進行處理。讓我們逐步分析一下:

  1. CompletableFuture.supplyAsync(() -> { ... })
    • 這一行建立了一個 CompletableFuture 例項,並使用supplyAsync方法非同步執行提供的 lambda 表示式。
    • 在該 lambda 表示式中,程式碼呼叫TimeUnit.SECONDS.sleep(5)故意讓任務休眠 5 秒鐘,模擬一個耗時操作。
  2. .orTimeout(2, TimeUnit.SECONDS)
    • orTimeout方法設定了非同步任務的超時時間為 2 秒。如果任務在 2 秒內未完成,則會觸發超時並返回一個TimeoutException
  3. .exceptionally(ex -> "Timeout occurred")
    • exceptionally方法接受一個函式式介面Function作為引數,該函式接收非同步任務丟擲的異常作為輸入,並返回一個備用結果。
    • 在這裡,lambda 表示式ex -> "Timeout occurred"接收到異常例項ex後,返回字串"Timeout occurred"作為備用結果。
  4. .thenAccept(System.out::println);
    • thenAccept方法接受一個函式式介面Consumer作為引數,該介面消費上一個任務的結果,但不返回任何值。
    • 在這裡,使用System.out::println方法引用作為Consumer的實現,它將列印上一個任務的結果 (即備用結果"Timeout occurred"或成功結果"Result after delay"(如果任務在 2 秒內完成))。

當我們執行這個程式時,由於非同步任務會休眠 5 秒鐘,而超時時間設定為 2 秒鐘,因此會觸發超時。exceptionally方法會被呼叫,並返回備用結果"Timeout occurred"thenAccept方法,最終被列印到控制檯。

輸出應該是:

Timeout occurred

如果將超時時間設定為大於 5 秒,例如orTimeout(6, TimeUnit.SECONDS),那麼輸出將是:

Result after delay

這個示例展示瞭如何使用orTimeout方法來設定 CompletableFuture 的超時時間,以及如何使用exceptionally方法來處理超時情況。在一些需要控制任務執行時間的場景中,這個功能非常有用,可以防止任務無限期地阻塞或佔用資源。

結論

JavaExecutorServiceCompletableFuture是管理現代應用程式中併發性的強大工具。它們透過提供易於使用的任務管理、連結、錯誤處理和超時管理 API 來簡化非同步程式設計的複雜性。透過理解和利用這些實用程式,開發人員可以編寫高效、響應迅速且易於維護的併發應用程式。

  • 2021 年原創合集
  • 2022 年原創合集
  • 2023 年原創合集
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go、Python
  • 單元&白盒&工具合集
  • 測試方案&BUG&爬蟲&UI 自動化
  • 測試理論雞湯
  • 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章