Java中CompletableFuture與虛擬執行緒比較

banq發表於2024-04-13

非同步程式設計是現代 Java 應用程式的基石,允許它們在不阻塞主執行緒的情況下處理任務。但Java 21帶來了新的挑戰者:虛擬執行緒。這些傳統作業系統執行緒的輕量級替代方案有望顯著提高效能。然而,熟悉的 CompletableFuture 仍然是非同步操作的強大工具。本文深入探討了這兩種方法的優點和缺點,幫助您為 Java 非同步戰爭選擇正確的武器!

背景
在當今快節奏的世界中,應用程式需要響應迅速且高效。傳統的同步程式設計(主執行緒等待每個任務完成後再繼續)可能會導致效能下降。這就是非同步程式設計的用武之地!

非同步程式設計允許 Java 應用程式同時處理多個任務,而不會阻塞主執行緒。想象一下伺服器正在等待資料庫查詢完成。非同步程式設計不會凍結整個應用程式,而是讓伺服器在資料庫查詢在後臺執行時繼續處理其他請求。這可以保持應用程式的響應能力並提供更流暢的使用者體驗。

隨著 Java 21 中虛擬執行緒的引入,非同步程式設計的格局即將發生重大變化。這些傳統作業系統 (OS) 執行緒的輕量級替代方案有望提高效能和資源利用率。

然而,非同步程式設計工具箱中成熟的工具 CompletableFuture 仍然是一個強大的選擇。它提供了一種處理非同步任務的結構化方法,並已在 Java 開發中廣泛採用。

本文深入研究 CompletableFuture 和虛擬執行緒的世界,探討它們的優點和缺點。我們將比較和對比這些方法,以幫助您選擇正確的武器來處理 Java 應用程式中的非同步操作。

CompletableFuture
CompletableFuture 是 Java 8 中引入的一個類,表示非同步計算的未來結果。它使您能夠編寫非同步、非阻塞程式碼,從而增強應用程式效能和響應能力。

  • 主要特徵:
    • 非同步任務鏈:
      • thenApply:轉換已完成的 CompletableFuture 的結果。
      • thenAccept:對結果執行操作。
      • thenCombine:合併兩個 CompletableFutures 的結果。
      • 用於複雜工作流程的更多方法。
    • 結果處理:
      • join:等待完成並檢索結果(可能阻塞)。
      • get:與 類似 join,但具有超時選項和異常處理。
    • 錯誤處理:
      • exceptionally:在發生錯誤時提供後備操作。
      • handle:處理成功完成和異常。


public static CompletableFuture<String> downloadFile(String url) {
  return CompletableFuture.supplyAsync(() -> {
    <font>// Simulate downloading a file<i>
    try {
      Thread.sleep(2000);
// Simulate download time<i>
      return
"File downloaded successfully!";
    } catch (InterruptedException e) {
      throw new RuntimeException(
"Download interrupted!");
    }
  });
}
 
public static void main(String[] args) {
  downloadFile(
"https://example.com/file.txt")
      .thenAccept(result -> System.out.println(result))
      .exceptionally(error -> {
        System.err.println(
"Error downloading file: " + error.getMessage());
        return null;
      });
 
  System.out.println(
"Doing other tasks while downloading...");
}

在這個例子中:

  • downloadFile 返回一個 CompletableFuture 表示最終下載完成。
  • thenAccept 用於處理成功下載結果(列印訊息)。
  • exceptionally 定義如果下載過程中發生錯誤該怎麼辦(列印錯誤訊息)。
  • 主程式可以繼續執行任務 ( System.out.println("Doing other tasks while downloading...")),因為它不會被阻塞等待下載完成。


需要考慮的限制:

  • 執行緒池開銷:  CompletableFuture 依賴執行緒池來執行非同步任務。雖然與作業系統執行緒相比是輕量級的,但仍然存在一些與管理執行緒池相關的開銷。
  • 上下文繼承: 預設情況下,CompletableFuture 本身並不從呼叫執行緒繼承上下文(如安全憑證)。如果非同步操作需要,您可能需要顯式傳遞此上下文。

儘管存在這些限制,CompletableFuture 仍然是 Java 非同步程式設計的一個有價值的工具。然而,Java 21 中引入的虛擬執行緒為非同步領域提供了一個新的競爭者。接下來我們就來探索一下它們吧!

虛擬執行緒
雖然 CompletableFuture 提供了一種結構化的非同步程式設計方法,但 Java 21 引入了一個遊戲規則改變者:虛擬執行緒。這些創新執行緒與傳統作業系統執行緒有很大不同,帶來了多種優勢。
傳統作業系統執行緒與虛擬執行緒:兩個世界的故事

  • 作業系統執行緒: 這些是由作業系統直接管理的重量級實體。建立和管理它們可能會佔用大量資源,尤其是在處理大量併發任務時。
  • 虛擬執行緒: 這些是在 Java 虛擬機器 (JVM) 本身內執行的輕量級替代方案。它們不直接對映到作業系統執行緒,從而大大減少了建立和管理它們所需的資源。

輕量級虛擬執行緒的力量
與傳統作業系統執行緒相比,虛擬執行緒具有以下幾個優點:

  • 輕量級建立: 與作業系統執行緒相比,建立和管理虛擬執行緒速度更快,資源消耗更少。這使您可以擁有更大的併發任務池,而無需顯著的開銷。
  • 高效的資源利用: 由於虛擬執行緒是輕量級的,因此它們需要更少的系統資源。這意味著整體應用程式效能和可擴充套件性的提高,尤其是在資源受限的系統上。
  • 改進的可擴充套件性: 憑藉其輕量級的特性,虛擬執行緒允許應用程式有效地處理大量併發任務。這對於處理大量非同步操作的應用程式特別有利。

下面是一個示例(為了說明目的而進行了簡化),演示瞭如何使用虛擬執行緒來執行與 CompletableFuture 示例類似的下載任務:

public static void downloadFileVirtualThread(String url) {
  new VirtualThread(() -> {
    <font>// Simulate downloading a file<i>
    try {
      Thread.sleep(2000);
// Simulate download time<i>
      System.out.println(
"File downloaded successfully!");
    } catch (InterruptedException e) {
      System.err.println(
"Download interrupted!");
    }
  }).start();
}
 
public static void main(String[] args) {
  downloadFileVirtualThread(
"https://example.com/file.txt");
 
  System.out.println(
"Doing other tasks while downloading...");
}

在這個例子中:
  • downloadFileVirtualThread 建立一個新的 VirtualThread 物件併為其分配下載任務(類似於 lambda 函式)。
  • start() 開始執行虛擬執行緒。
  • 主程式可以繼續執行任務(System.out.println("Doing other tasks while downloading...")),因為虛擬執行緒非同步處理下載。

需要考慮的限制

  • 早期開發階段: 虛擬執行緒是 Java 中相對較新的功能。儘管前景廣闊,但它們仍處於開發階段,並且 API 可能會在未來的 Java 版本中不斷髮展。
  • 與 CompletableFuture 相比的成熟度:  CompletableFuture 已經存在了一段時間,並且圍繞它構建了更成熟的庫和工具生態系統。虛擬執行緒在這方面仍在迎頭趕上。

儘管虛擬執行緒令人興奮,但承認它們的侷限性也很重要。 CompletableFuture 仍然是一個成熟的工具,擁有良好的記錄。

何時使用 CompletableFuture 或虛擬執行緒
執行緒模型   :

  • CompletableFuture依賴底層執行緒池(作業系統執行緒)    
  • 虛擬執行緒由 JVM 管理的輕量級執行緒

開銷    
  • CompletableFuture執行緒池管理的開銷適中  
  • 虛擬執行緒建立和管理執行緒的開銷非常低

複雜    
  • CompletableFuture相對簡單的API    
  • 虛擬執行緒稍微複雜的 API(Java 21 中的新概念)

錯誤處理    
  • CompletableFuture提供處理異常的機制    
  • 虛擬執行緒錯誤處理的工作方式與傳統執行緒類似

上下文繼承    
  • CompletableFuture本身並不從呼叫執行緒繼承上下文    
  • 虛擬執行緒可以從父執行緒繼承上下文

CompletableFuture 的場景:

  • 現有程式碼庫: 如果您的程式碼庫已經有效地利用了 CompletableFuture,則可能不需要立即切換到虛擬執行緒。
  • 更簡單的非同步任務: 對於不需要極高資源效率的更簡單的非同步操作,CompletableFuture 可能是一個不錯的選擇,因為它具有熟悉的 API。
  • 繁重CPU計算 與併發 平行計算結合

虛擬執行緒的場景:
  • 高度併發任務: 處理大量併發操作的應用程式可以從虛擬執行緒的輕量級特性中受益匪淺。
  • 提高資源利用率: 如果資源效率是重中之重,虛擬執行緒可以幫助您以更少的系統資源需求處理更多併發任務。
  • 面向未來: 隨著虛擬執行緒的成熟,它們有可能成為 Java 非同步程式設計的主要方法。
  • 只適合IO輸入輸出的堵塞場景 取代NIO 等socket

選擇正確的工具:
CompletableFuture 和虛擬執行緒都有各自的優點和缺點。最佳選擇取決於您的具體專案要求:

  • 對於具有更簡單的非同步任務並注重可維護性的現有程式碼庫,CompletableFuture 可能是一個不錯的選擇。
  • 對於需要高併發性、資源效率和麵向未來的應用程式,虛擬執行緒提供了一個令人信服的替代方案。


 

相關文章