自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 21 的虛擬線程:高性能并發(fā)應(yīng)用的福音

開發(fā) 前端
盡管Scoped Values (JEP 446) 和Structured Concurrency (JEP 453) 仍然是Java 21中的預(yù)覽功能,但Virtual Threads已經(jīng)成為一個成熟的、適用于生產(chǎn)環(huán)境的功能。

Java 21 最重要的特性之一就是虛擬線程 (JEP 444)。這些輕量級的線程降低了編寫、維護(hù)和觀察高吞吐量并行應(yīng)用所需的努力。

在討論新特性之前,讓我們先看一下當(dāng)前的狀態(tài),以便更好地理解它試圖解決什么問題以及帶來了哪些好處。

平臺線程

在引入虛擬線程之前,我們習(xí)慣使用的線程是 java.lang.Thread,它背后是所謂的平臺線程 (platform threads)。

這些線程通常與操作系統(tǒng)調(diào)度的內(nèi)核線程一一映射。操作系統(tǒng)線程相當(dāng)“重”,這使得它們適合執(zhí)行所有類型的任務(wù)。

根據(jù)操作系統(tǒng)和配置,它們默認(rèn)情況下會消耗大約2到10 MB的內(nèi)存。因此,如果你想在高負(fù)載并發(fā)應(yīng)用程序中使用一百萬個線程,最好要有超過2 TB的可用內(nèi)存!

這存在一個明顯的瓶頸,限制了我們實(shí)際可以在沒有缺點(diǎn)的情況下?lián)碛械木€程數(shù)量。

每個請求一個線程

這很成問題,因?yàn)樗苯优c典型的服務(wù)器應(yīng)用程序“每個請求一個線程”的方法相沖突。使用每個請求一個線程有很多優(yōu)點(diǎn),例如更簡單的狀態(tài)管理和清理。但它也創(chuàng)造了可擴(kuò)展性限制。應(yīng)用程序的“并發(fā)單位”,在這種情況下是一個請求,需要一個“平臺并發(fā)單位”。因此,線程很容易被原始CPU能力或網(wǎng)絡(luò)耗盡。

即使“每個請求一個線程”有許多優(yōu)點(diǎn),共享重量級的線程可以更均勻地利用硬件,但也需要一種完全不同的方法。

異步救援

而不是在單個線程上運(yùn)行整個請求,它的每個部分都從池中使用一個線程,當(dāng)它們的任務(wù)完成時,另一個任務(wù)可能會重用同一個線程。這允許代碼需要更少的線程,但引入了異步編程的負(fù)擔(dān)。

異步編程伴隨著它自己的范例,具有一定的學(xué)習(xí)曲線,并且可能會使程序更難理解和跟蹤。請求的每個部分可能都在不同的線程上執(zhí)行,從而創(chuàng)建沒有合理上下文的堆棧跟蹤,并使調(diào)試某些內(nèi)容變得非常棘手甚至幾乎不可能。

Java有一個用于異步編程的優(yōu)秀API,CompletableFuture。但這是一個復(fù)雜的API,并且不太適合許多Java開發(fā)人員習(xí)慣的思維方式。

重新審視“每個請求一個線程”模型,很明顯,一種更輕量級的線程方法可以解決瓶頸并提供一種熟悉的做事方式。

輕量級線程

由于平臺線程的數(shù)量是無法在沒有更多硬件的情況下改變的,因此需要另一個抽象層,切斷可怕的 1:1 映射,它是首先造成瓶頸的原因。

輕量級線程不與特定的平臺線程綁定,也不會伴隨大量的預(yù)分配內(nèi)存。它們由運(yùn)行時而不是底層操作系統(tǒng)調(diào)度和管理。這就是為什么可以創(chuàng)建大量輕量級線程的原因。

這個概念并不新鮮,許多語言都采用某種形式的輕量級線程:

  • Go 語言中的 Goroutine
  • Erlang 進(jìn)程
  • Haskell 線程
  • 等等

Java最終于第21版中引入了自己的輕量級線程實(shí)現(xiàn):虛擬線程 (Virtual Threads)。

虛擬線程

虛擬線程是一種新的輕量級java.lang.Thread變體,是Project Loom的一部分,它不是由操作系統(tǒng)管理或調(diào)度的。相反,JVM負(fù)責(zé)調(diào)度。

當(dāng)然,任何實(shí)際的工作都必須在平臺線程中運(yùn)行,但是JVM使用所謂的“載體線程”(carrier threads) 來“攜帶”任何虛擬線程,以便在它們需要執(zhí)行時執(zhí)行這些線程。

圖片圖片

JVM/操作系統(tǒng)線程調(diào)度器

所需的平臺線程在一個 FIFO 工作竊取 ForkJoinPool 中進(jìn)行管理,該池默認(rèn)情況下使用所有可用的處理器,但可以通過調(diào)整系統(tǒng)屬性jdk.virtualThreadScheduler.parallelism來根據(jù)需求進(jìn)行修改。

ForkJoinPool與其他功能(例如并行流)使用的通用池之間的主要區(qū)別在于,通用池以LIFO模式運(yùn)行。

廉價(jià)且豐富的線程

擁有廉價(jià)且輕量級的線程,可以使用“每個請求一個線程”模型,而不必?fù)?dān)心實(shí)際需要多少個線程。如果你的代碼在虛擬線程中調(diào)用阻塞 I/O 操作,則運(yùn)行時會掛起虛擬線程,直到它可以稍后恢復(fù)。

這樣,硬件就可以被優(yōu)化到幾乎最佳的水平,從而實(shí)現(xiàn)高水平的并發(fā)性,因此也實(shí)現(xiàn)高吞吐量。

因?yàn)樗鼈兎浅A畠r(jià),所以虛擬線程不會被重用或需要池化。每個任務(wù)都由其自己的虛擬線程表示。

設(shè)置邊界

調(diào)度器負(fù)責(zé)管理載體線程,因此需要一定的邊界和分離,以確??赡艿摹盁o數(shù)”虛擬線程按照預(yù)期運(yùn)行。這是通過在載體線程及其可能攜帶的任何虛擬線程之間不保持線程關(guān)聯(lián)來實(shí)現(xiàn)的:

  • 虛擬線程無法訪問載體,Thread.currentThread() 返回虛擬線程本身。
  • 堆棧跟蹤是分開的,任何在虛擬線程中拋出的異常只包含其自己的堆棧幀。
  • 虛擬線程的線程局部變量對它的載體不可用,反之亦然。
  • 從代碼的角度來看,載體及其虛擬線程共享一個平臺線程是不可見的。

讓我們看看代碼

使用Virtual Threads最大的好處是,你不需要學(xué)習(xí)新的范例或復(fù)雜的API,就像使用異步編程一樣。相反,你可以像對待非虛擬線程一樣處理它們。

創(chuàng)建平臺線程

創(chuàng)建平臺線程很簡單,就像使用 Runnable 創(chuàng)建一樣:

Runnable fn = () -> {
    // your code here
};

Thread thread = new Thread(fn).start();

隨著Project Loom簡化了新的并發(fā)方法,它還提供了一種創(chuàng)建平臺支持線程的新方法:

Thread thread = Thread.ofPlatform().
                      .start(runnable);

實(shí)際上,現(xiàn)在還有一個完整的fluent API,因?yàn)閛fPlatform()會返回一個Thread.Builder.OfPlatform實(shí)例:

Thread thread = Thread.ofPlatform().
                      .daemon()
                      .name("my-custom-thread")
                      .unstarted(runnable);

但你肯定不是來學(xué)習(xí)創(chuàng)建“舊”線程的新方法的,我們想要一點(diǎn)新的東西。繼續(xù)看。

創(chuàng)建虛擬線程

對于虛擬線程,也有類似的fluent API:

Runnable fn = () -> {
  // your code here
};

Thread thread = Thread.ofVirtual(fn)
                      .start();

除了構(gòu)建器方法之外,你還可以直接使用以下方式執(zhí)行Runnable:

Thread thread = Thread.startVirtualThread(() -> {
  // your code here
});

由于所有虛擬線程始終是守護(hù)線程,因此如果你想在主線程上等待,請不要忘記調(diào)用join()。

創(chuàng)建虛擬線程的另一種方法是使用 Executor:

var executorService = Executors.newVirtualThreadPerTaskExecutor();

executorService.submit(() -> {
  // your code here
});

小結(jié)

盡管Scoped Values (JEP 446) 和Structured Concurrency (JEP 453) 仍然是Java 21中的預(yù)覽功能,但Virtual Threads已經(jīng)成為一個成熟的、適用于生產(chǎn)環(huán)境的功能。

它們是Java并發(fā)的一種通用且強(qiáng)大的新方法,將對我們未來的程序產(chǎn)生重大影響。它們使用了熟悉的和可靠的“每個請求一個線程”方法,同時以最優(yōu)化的方式利用所有可用硬件,而不需要學(xué)習(xí)新的范例或復(fù)雜的API。

責(zé)任編輯:武曉燕 來源: 程序新視界
相關(guān)推薦

2023-10-09 08:18:08

域值Java 21結(jié)構(gòu)化

2025-01-03 16:32:13

SpringBoot虛擬線程Java

2023-10-23 19:51:11

Java線程

2024-12-04 10:58:57

TomcatJetty高并發(fā)

2021-07-27 16:01:29

高并發(fā)定時器高性能

2023-09-26 12:16:29

JDK 21Java

2017-11-27 09:14:29

2023-11-06 08:32:17

FastAPIPython

2016-12-21 09:33:40

2021-01-03 09:58:39

StampedLock線程開發(fā)技術(shù)

2011-12-15 13:28:57

2015-09-23 09:04:30

java高性能

2024-10-06 14:37:52

2023-12-28 08:45:25

虛擬線程Java 21

2020-11-10 07:46:09

服務(wù)器高并發(fā)高性能

2024-10-07 08:40:56

Spring應(yīng)用程序Java

2024-09-06 07:55:42

2009-01-12 09:22:40

虛擬化高性能計(jì)算HPC

2024-03-20 08:00:00

軟件開發(fā)Java編程語言

2015-09-17 18:51:31

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號