擁抱虛擬執行緒 (Embracing Virtual Threads)

工程 (Engineering) | Mark Paluch | 2022 年 10 月 11 日 (October 11, 2022) | ...

Project Loom 已經透過 JEP 425 進入 JDK。自 2022 年 9 月的 Java 19 以預覽功能提供。其目標是大幅減少編寫、維護和觀察高吞吐量並行應用程式的工作量。

虛擬執行緒適用於何處 (Where Virtual Threads make sense)

這使得輕量級虛擬執行緒成為應用程式開發人員和 Spring Framework 令人興奮的方法。過去幾年顯示,應用程式趨勢是透過網路相互通訊。許多應用程式使用資料儲存區、訊息佇列和遠端服務。I/O 密集型應用程式是主要受益於虛擬執行緒的應用程式,前提是它們是使用阻塞 I/O 功能(例如 InputStream 和同步 HTTP、資料庫和訊息佇列用戶端)構建的。在虛擬執行緒上執行這些工作負載有助於減少與平台執行緒相比的記憶體佔用空間,並且在某些情況下,虛擬執行緒可以提高並行性。

如果系統具有並行性所需的額外資源,則可以實現更高的並行性。具體而言,這些是:

  1. 連接池中的可用連線 (Available connections in a connection pool)

  2. 足夠的記憶體來處理增加的負載 (Sufficient memory to serve the increased load)

  3. 未使用的 CPU 時間 (Unused CPU time)

顯然,虛擬執行緒的使用不僅限於直接減少記憶體佔用空間或提高並行性。虛擬執行緒的引入還促使人們更廣泛地重新審視僅在平台執行緒可用時為執行時期所做的決策。

並行性工具的修訂 (Revision of Concurrency Utilities)

如果未配置 ThreadFactory,Spring Framework 的 SimpleAsyncTaskExecutor 會為提交的每個 runnable 使用一個新的平台執行緒。這種安排需要建立平台執行緒,導致吞吐量降低和記憶體消耗增加。預設情況下,可以修改 SimpleAsyncTaskExecutor 以使用虛擬執行緒,以減少記憶體佔用空間並提高其預設配置中的吞吐量。(同時,可以使用自訂的 TaskExecutor 變體來達到相同的效果。)

程式設計模型的修訂 (Revision of Programming Models)

虛擬執行緒可以改變我們對異步程式設計介面的看法。如果我們從程式碼在虛擬執行緒上運行的假設開始,那麼在許多情況下,使用異步程式設計模型的原因就會消失。分配虛擬執行緒要輕量得多,而且執行緒數量不再是可擴展性的主要限制。更清楚地說,異步程式設計模型不會消除網路呼叫的延遲。異步 Apache HTTP 用戶端或 netty 只是在網路呼叫無法進行時切換任務,而不是阻塞執行緒。虛擬執行緒也會發生同樣的情況:它們有效地讓步於另一個可以繼續工作的 Runnable

Project Loom 重新審視了 Java 執行時期程式庫中所有可能阻塞的區域,並更新了程式碼,以便在程式碼遇到阻塞時讓步。Java 的並行性工具(例如 ReentrantLockCountDownLatchCompletableFuture)可以在虛擬執行緒上使用,而不會阻塞底層平台執行緒。此變更使 Future.get().get(Long, TimeUnit) 在虛擬執行緒上成為良好的公民,並消除了對 Future 的回呼驅動使用的需求。

導致異步 Servlet API 的假設可能會隨著虛擬執行緒的引入而失效。引入異步 Servlet API 是為了釋放伺服器執行緒,以便伺服器可以繼續處理請求,而工作執行緒繼續處理請求。在虛擬執行緒上運行 servlet 請求和響應處理消除了釋放伺服器執行緒的需要,從而引發了一個問題,即為什麼要使用 ServletRequest.startAsync(),因為異步分叉涉及大量狀態儲存,如果不再需要,則可以消除。

減輕限制 (Mitigating Limitations)

我們的團隊一直在試用虛擬執行緒,因為它們被稱為 Fibers。從那時起,直到 Java 19 的發布,一個限制仍然很普遍,導致平台執行緒固定,從而在使用 synchronized 時有效地降低了並行性。使用 synchronized 程式碼區塊本身不是問題;僅當這些區塊包含阻塞程式碼(一般來說是 I/O 操作)時。這些安排可能會出現問題,因為載體平台執行緒是有限的資源,並且在沒有仔細檢查工作負載的情況下在虛擬執行緒上運行程式碼時,平台執行緒固定可能會導致應用程式效能下降。事實上,即使沒有虛擬執行緒,同步區塊中的相同阻塞程式碼也可能導致效能問題。

Spring Framework 大量使用 synchronized 來實現鎖定,主要是在本機資料結構周圍。多年來,在虛擬執行緒可用之前,我們修改了可能與第三方資源交互的 synchronized 區塊,從而消除了高度並行應用程式中的鎖定爭用。因此,由於其龐大的社群和來自現有並行應用程式的大量回饋,Spring 已經處於相當不錯的狀態。為了成為虛擬執行緒場景中最好的公民,我們將在 I/O 或其他阻塞程式碼的上下文中進一步重新審視 synchronized 的使用,以避免在熱程式碼路徑中固定平台執行緒,以便您的應用程式可以充分利用 Project Loom。

在虛擬執行緒上運行 Spring 應用程式 (Running Spring Applications on Virtual Threads)

使用最新版本的 Spring Framework、Spring Boot 和 Apache Tomcat,您可以開始自行試驗。您可以開始分析虛擬執行緒如何影響您的應用程式工作負載,並基準測試虛擬執行緒與平台執行緒的使用情況。若要自訂您的 Spring Boot 應用程式以在虛擬執行緒上處理 servlet 請求,請套用以下自訂:

@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor asyncTaskExecutor() {
  return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}

@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
  return protocolHandler -> {
    protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
  };
}

我們正在盡一切努力使預覽體驗在目前盡可能無縫,並且我們希望在 Loom 在新的 OpenJDK 版本中退出預覽後提供一流的配置選項。

如果我們了解核心框架中虛擬執行緒導向優化的具體潛力,無論是某些 synchronized 使用點還是某些 ThreadLocal 使用,我們都會盡可能在即將推出的 Spring Framework 和 Spring Boot 維護版本中推出相應的改進,即使在 Loom 普遍可用之前也是如此。

虛擬執行緒不僅影響 Spring Framework,還影響所有周圍的整合,例如資料庫驅動程式、訊息傳遞系統、HTTP 用戶端等等。許多這些專案都知道需要改進其 synchronized 行為,以充分釋放 Project Loom 的潛力。

您的應用程式會受益於虛擬執行緒嗎? (Will your application benefit from Virtual Threads?)

這是一個比是否會有好處更具體的問題,也是一個更難回答的問題。

我們可以說,最有可能的情況是,如果您目前根本沒有做任何異步操作(甚至沒有 Servlet 3.1 樣式的異步請求,否則您可能需要進行一些修訂以更好地對齊),那麼您幾乎不需要進行任何更改就可以受益。當然,必須有一些實際的 I/O 或其他執行緒停車,Loom 才能帶來好處。

我們也相信,ReactiveX 風格的 API 仍然是一種強大的方式,可以用來組合並行邏輯,也是處理串流的自然方式。我們認為,虛擬執行緒可以補充反應式程式設計模型,消除阻塞 I/O 的障礙。然而,單純使用虛擬執行緒來處理無限串流仍然是一個挑戰。對於需要宣告式並行(例如分散-收集)的並行場景,ReactiveX 是一個合適的方法。底層的 Reactive Streams 規範定義了一種協議,用於數據管道的需求、反壓和取消,而無需將自身限制於非阻塞 API 或特定的執行緒使用方式。

我們非常期待來自應用程式的集體經驗和回饋。目前我們的重點是確保您能夠開始自行實驗。如果您在使用虛擬執行緒的早期實驗中遇到任何特定問題,請將其報告給相應的專案。

嘗試在基於 Spring 的應用程式中使用虛擬執行緒,並告訴我們結果如何!

取得 Spring 電子報

透過 Spring 電子報保持聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉辦的活動

查看 Spring 社群中所有即將舉辦的活動。

查看全部