[英]Project loom: what makes the performance better when using virtual threads?
[英]What exactly makes Java Virtual Threads better
我很喜歡 Project Loom,但有一件事我不能完全理解。
大多數 Java 服務器使用具有一定線程限制(200、300 ..)的線程池,但是您不受操作系統的限制以產生更多,我已經閱讀了 Linux 的特殊配置,您可以達到巨大的數量。
操作系統線程更昂貴,啟動/停止速度更慢,必須處理上下文切換(由它們的數量放大),並且您依賴於可能拒絕為您提供更多線程的操作系統。
話雖如此,虛擬線程也消耗相似數量的 memory (或者至少這是我所理解的)。 使用 Loom,我們得到了尾調用優化,這應該會減少 memory 的使用。 同步和線程上下文復制也應該是一個類似大小的問題。
事實上,您能夠產生數百萬個虛擬線程
public static void main(String[] args) {
for (int i = 0; i < 1_000_000; i++) {
Thread.startVirtualThread(() -> {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
當我使用平台線程時,上面的代碼會在 25k 左右出現 OOM 異常。
我的問題是,究竟是什么讓這些線程如此輕巧,是什么阻止我們生成 100 萬個平台線程並與它們一起工作,是不是只有上下文切換才使常規線程如此“沉重”。
一個非常相似的問題
到目前為止我發現的東西:
協程(即虛擬線程)的一大優點是它們可以生成高水平的並發性,而沒有回調的缺點。
我先介紹一下利特爾定律:
concurrency = arrival_rate * latency
我們可以將其重寫為:
arrival_rate = concurrency/latency
在穩定的系統中,到達率等於吞吐量。
throughput = concurrency/latency
要提高吞吐量,您有 2 個選項:
對於常規線程,由於上下文切換開銷,很難通過阻塞調用達到高並發性。 在某些情況下可以異步發出請求(例如 NIO + Epoll 或 Netty io_uring 綁定),但是您需要處理回調和回調地獄。
使用虛擬線程,可以異步發出請求並停放虛擬線程並調度另一個虛擬線程。 一旦收到響應,虛擬線程就會被重新調度,並且這是完全透明的。 編程 model 比使用經典線程和回調更直觀。
從根本上說,線程的任何實現,無論是輕量級還是重量級,都依賴於兩個結構
線程有兩種任務類型
並發與操作系統調度程序有關,並且在線程生命周期中具有非阻塞 IO 任務(與並行性不同)。 I/O 綁定程序與 CPU 綁定程序相反。 此類程序大部分時間都在等待輸入或 output 操作在 CPU 空閑時完成。 I/O 操作可以包括從主 memory 或網絡接口寫入或讀取的操作。
NIO 編程范式是我們談論並發性時首先想到的主題之一(從 2011 年 7 月開始,JDK 7 在 JDK 8 中部分和完全引入),因此多線程和管理線程的生命周期從線程池和適當的回調大致說我們可以實現這一點。
但另一方面,JVM 線程(基於守護進程和用戶)都包裹在操作系統線程上,操作系統線程是昂貴的資源,我們對它們的適用數量有限制。 這是虛擬線程或項目織機實踐。
在 OpenJDK 的最新原型中,一個名為 Fiber 的新 class 與線程 class 一起被引入庫。 由於計划中的 Fibers 庫與 Thread 相似,因此用戶實現也應該保持相似。 但是,有兩個主要區別:
你可以在這里找到更多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.