最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java虛擬線程原理與實(shí)踐指南(含實(shí)際案例)

 更新時(shí)間:2026年06月06日 09:10:22   作者:BlueSea?每日coding  
Java虛擬線程是一種輕量級(jí)線程,由JVM管理而非操作系統(tǒng),這篇文章主要介紹了Java虛擬線程原理與實(shí)踐指南的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

Java虛擬線程(Virtual Threads)是近年來Java生態(tài)中最引人矚目的變革之一。自JDK 21正式發(fā)布以來,這項(xiàng)由Project Loom孕育的特性正在重塑高并發(fā)應(yīng)用的開發(fā)方式。本文將從原理出發(fā),結(jié)合實(shí)際案例,探討虛擬線程帶來的性能提升與實(shí)踐要點(diǎn)。

一、虛擬線程:為什么我們需要它?

1.1 傳統(tǒng)線程模型的困境

在理解虛擬線程的價(jià)值前,我們需要先回顧傳統(tǒng)Java線程模型的局限。長(zhǎng)期以來,Java采用"一對(duì)一"的線程模型——每個(gè)java.lang.Thread實(shí)例直接映射為一個(gè)操作系統(tǒng)(OS)線程。這種設(shè)計(jì)在面臨海量并發(fā)I/O任務(wù)時(shí),其短板便顯露無遺:

  • 資源消耗巨大:每個(gè)OS線程通常占用1 MB左右的內(nèi)存(主要是??臻g),這意味著一個(gè)JVM若要?jiǎng)?chuàng)建一萬個(gè)線程,僅線程棧就要消耗約10 GB內(nèi)存。

  • 上下文切換沉重:OS調(diào)度器在大量線程間切換時(shí),需要保存/恢復(fù)CPU寄存器、程序計(jì)數(shù)器等上下文,線程數(shù)越多,調(diào)度開銷越呈指數(shù)級(jí)增長(zhǎng)。

  • 阻塞即浪費(fèi):當(dāng)線程執(zhí)行I/O操作(如數(shù)據(jù)庫(kù)查詢、文件讀寫)而被阻塞時(shí),底層的OS線程只能等待,無法執(zhí)行其他任務(wù)。

正因如此,開發(fā)者不得不轉(zhuǎn)向異步編程模型(如CompletableFuture、響應(yīng)式框架),試圖通過非阻塞I/O提升資源利用率。然而,這種方式雖提高了伸縮性,卻帶來了代碼復(fù)雜性問題。

1.2 虛擬線程的破局思路

虛擬線程的核心思想可概括為:將線程的管理從OS層上移到JVM層。它實(shí)現(xiàn)了N:M的調(diào)度模型——大量(N)虛擬線程運(yùn)行在少量(M)平臺(tái)線程(即傳統(tǒng)OS線程)之上。這些承載虛擬線程運(yùn)行的平臺(tái)線程被稱為載體線程(Carrier Threads)

虛擬線程的輕量級(jí)特性令人印象深刻:每個(gè)虛擬線程僅占用數(shù)百字節(jié)到幾KB的內(nèi)存,這意味著在16 GB內(nèi)存的服務(wù)器上,理論上可以創(chuàng)建數(shù)百萬個(gè)虛擬線程——這是傳統(tǒng)線程模型難以企及的量級(jí)。

二、深入原理:虛擬線程如何實(shí)現(xiàn)"阻塞不阻塞"?

虛擬線程的神奇之處在于它如何處理阻塞操作。當(dāng)一個(gè)虛擬線程執(zhí)行阻塞I/O時(shí),JVM會(huì)上演一場(chǎng)精妙的"偷梁換柱":

2.1 Continuation:暫停與恢復(fù)的基石

虛擬線程的本質(zhì)是協(xié)程(coroutine)的一種實(shí)現(xiàn),其底層依賴于JVM引入的Continuation(續(xù)體)機(jī)制。Continuation可以理解為一段執(zhí)行代碼的"快照",它保存了線程的執(zhí)行狀態(tài),包括程序計(jì)數(shù)器、局部變量和操作數(shù)棧等。

當(dāng)虛擬線程遇到阻塞操作時(shí),執(zhí)行流程如下:

  1. 識(shí)別阻塞點(diǎn):JVM運(yùn)行時(shí)檢測(cè)到虛擬線程將執(zhí)行阻塞I/O(如Socket.read())。

  2. 保存上下文:將當(dāng)前虛擬線程的執(zhí)行狀態(tài)封裝成Continuation對(duì)象保存到堆內(nèi)存中。

  3. 卸載(Unmount):虛擬線程從其載體線程上卸載,載體線程被釋放。

  4. 載體線程復(fù)用:被釋放的載體線程立即從調(diào)度隊(duì)列中抓取下一個(gè)就緒的虛擬線程執(zhí)行。

  5. 異步I/O等待:底層的I/O操作以非阻塞方式進(jìn)行,JVM注冊(cè)回調(diào)等待完成。

  6. 重新掛載(Remount):I/O完成后,JVM將對(duì)應(yīng)的虛擬線程放回調(diào)度隊(duì)列。當(dāng)有空閑載體線程時(shí),虛擬線程被掛載上去,從上次中斷點(diǎn)恢復(fù)執(zhí)行。

這一機(jī)制的關(guān)鍵在于:阻塞的是虛擬線程,而非底層的平臺(tái)線程。載體線程永遠(yuǎn)不會(huì)因虛擬線程的I/O等待而空閑,始終在履行計(jì)算任務(wù)。

2.2 載體線程與調(diào)度

默認(rèn)情況下,JVM使用ForkJoinPool作為虛擬線程的調(diào)度器,平臺(tái)線程池的大小通常與CPU核心數(shù)相關(guān)。這個(gè)調(diào)度池負(fù)責(zé)將海量虛擬線程合理地分配到有限的平臺(tái)線程上執(zhí)行。

需要注意的是,虛擬線程并非適用于所有場(chǎng)景。對(duì)于CPU密集型任務(wù),虛擬線程的優(yōu)勢(shì)不明顯,因?yàn)樗鼈儠?huì)長(zhǎng)時(shí)間占用載體線程,反而可能因調(diào)度層增加額外開銷。虛擬線程的真正主場(chǎng)是I/O密集型應(yīng)用——這正是絕大多數(shù)業(yè)務(wù)系統(tǒng)的典型特征。

2.3 釘?。≒inning)問題

在特定情況下,虛擬線程無法被卸載,會(huì)"釘住"其載體線程。常見場(chǎng)景包括:

  • synchronized方法或代碼塊中執(zhí)行阻塞操作

  • 執(zhí)行native方法

釘住會(huì)阻礙載體線程服務(wù)其他虛擬線程,嚴(yán)重時(shí)可能導(dǎo)致載體線程池饑餓,降低系統(tǒng)擴(kuò)展性。因此,官方推薦在虛擬線程中使用java.util.concurrent.locks.ReentrantLock替代synchronized,后者能感知虛擬線程調(diào)度,避免釘住問題。

三、性能提升:數(shù)字會(huì)說話

虛擬線程的性能優(yōu)勢(shì)已有大量實(shí)測(cè)數(shù)據(jù)支持。以下是幾個(gè)關(guān)鍵數(shù)據(jù)點(diǎn):

3.1 學(xué)術(shù)基準(zhǔn)測(cè)試

愛爾蘭國(guó)家學(xué)院的一項(xiàng)碩士研究對(duì)虛擬線程與平臺(tái)線程進(jìn)行了全面基準(zhǔn)測(cè)試,結(jié)果如下:

指標(biāo)提升幅度
吞吐量+60.79%
延遲-28.8%
內(nèi)存使用-36.36%
CPU利用率-14.29%

研究特別指出,在I/O密集型工作負(fù)載中,虛擬線程的優(yōu)勢(shì)尤為突出;而在CPU密集型場(chǎng)景下,兩者表現(xiàn)相當(dāng)。

3.2 Tomcat集成實(shí)測(cè)

當(dāng)Tomcat 11集成虛擬線程后,在32核服務(wù)器上的測(cè)試結(jié)果更為驚人:

指標(biāo)平臺(tái)線程模式虛擬線程模式提升幅度
吞吐量3,200 req/s28,000 req/s775%
平均響應(yīng)時(shí)間450 ms120 ms-73%
內(nèi)存占用2.8 GB1.2 GB-57%

3.3 實(shí)際項(xiàng)目反饋

一個(gè)實(shí)際的開源項(xiàng)目在升級(jí)到Java 25并使用虛擬線程后,HTML聚合任務(wù)的處理速度提升了26.32%。項(xiàng)目作者評(píng)論道:"對(duì)于已經(jīng)針對(duì)固定線程池調(diào)優(yōu)的場(chǎng)景,虛擬線程帶來的改變不大;但對(duì)于原先未充分調(diào)優(yōu)的部分,提升相當(dāng)明顯。"

3.4 需注意的邊界情況

不過,虛擬線程并非在所有場(chǎng)景下都表現(xiàn)更優(yōu)。OpenJDK郵件列表中的一個(gè)基準(zhǔn)測(cè)試顯示:對(duì)于響應(yīng)時(shí)間在9ms以內(nèi)的極短API調(diào)用,平臺(tái)線程的性能反而優(yōu)于虛擬線程。這表明,虛擬線程的調(diào)度開銷在任務(wù)極度短暫時(shí)可能成為瓶頸。但隨著任務(wù)耗時(shí)增加(如超過20ms),虛擬線程的優(yōu)勢(shì)開始顯現(xiàn)。

四、實(shí)戰(zhàn)案例:電商訂單服務(wù)改造

理論終須實(shí)踐檢驗(yàn)。讓我們通過一個(gè)電商訂單服務(wù)的改造案例,看看虛擬線程如何在真實(shí)項(xiàng)目中落地。

4.1 改造前:傳統(tǒng)線程池的痛點(diǎn)

某電商平臺(tái)在促銷期間遭遇性能瓶頸:

  • 訂單處理延遲高達(dá)12秒

  • 數(shù)據(jù)庫(kù)連接池頻繁耗盡

  • 線程轉(zhuǎn)儲(chǔ)顯示80%的線程阻塞在支付網(wǎng)關(guān)調(diào)用上

原有架構(gòu)采用典型的"每請(qǐng)求一線程"模型,使用固定大小的平臺(tái)線程池(最大500線程)。在高峰流量下,請(qǐng)求大量排隊(duì),響應(yīng)時(shí)間急劇上升。

4.2 改造方案:分層引入虛擬線程

團(tuán)隊(duì)決定分層引入虛擬線程,而非一刀切地全部替換:

  1. API網(wǎng)關(guān)層:保留平臺(tái)線程,確保鑒權(quán)等關(guān)鍵操作的安全性

  2. 訂單服務(wù)層:核心業(yè)務(wù)邏輯啟用虛擬線程

  3. 支付調(diào)用:使用CompletableFuture配合虛擬線程執(zhí)行器實(shí)現(xiàn)異步化

關(guān)鍵代碼改造如下:

// 改造前:同步阻塞的支付調(diào)用
public Order createOrder(OrderRequest request) {
    // 同步調(diào)用支付服務(wù),阻塞線程
    PaymentResult result = paymentClient.charge(request);
    return processPayment(result);
}

// 改造后:虛擬線程中執(zhí)行
public CompletableFuture<Order> createOrderAsync(OrderRequest request) {
    // 使用虛擬線程執(zhí)行器
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        return CompletableFuture.supplyAsync(() -> {
            PaymentResult result = paymentClient.charge(request);
            return processPayment(result);
        }, executor);
    }
}

4.3 改造效果

經(jīng)過改造,系統(tǒng)性能獲得顯著提升:

  • 吞吐量:從1,200訂單/分鐘提升至9,800訂單/分鐘(+716%

  • 99%響應(yīng)時(shí)間:從8.2秒降至1.3秒(-84%

  • 數(shù)據(jù)庫(kù)連接使用率:下降65%

更值得關(guān)注的是,核心業(yè)務(wù)代碼幾乎沒有改動(dòng)——團(tuán)隊(duì)依然使用同步、順序的編程風(fēng)格,卻獲得了接近異步編程的性能收益。

五、實(shí)踐指南:API使用與避坑

5.1 創(chuàng)建虛擬線程的兩種方式

JDK提供了簡(jiǎn)潔的API來創(chuàng)建虛擬線程:

方式一:Thread.ofVirtual().start()

適用于啟動(dòng)單個(gè)輕量任務(wù):

Thread vThread = Thread.ofVirtual()
    .name("order-processor")
    .start(() -> {
        // 業(yè)務(wù)邏輯
        System.out.println("處理訂單中...");
        Thread.sleep(Duration.ofMillis(100)); // 模擬I/O
    });
vThread.join();

方式二:Executors.newVirtualThreadPerTaskExecutor()(推薦)

這是處理海量并發(fā)任務(wù)的首選方式。它為每個(gè)提交的任務(wù)創(chuàng)建一個(gè)新的虛擬線程,省去了傳統(tǒng)線程池的調(diào)優(yōu)煩惱:

// 處理10,000個(gè)并發(fā)任務(wù)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> 
        executor.submit(() -> {
            // 每個(gè)任務(wù)在獨(dú)立的虛擬線程中執(zhí)行
            processRequest(i);
        })
    );
} // try-with-resources 自動(dòng)關(guān)閉并等待完成

5.2 兩大陷阱與應(yīng)對(duì)

陷阱一:synchronized導(dǎo)致的釘住問題

當(dāng)虛擬線程進(jìn)入synchronized塊時(shí)會(huì)被釘在載體線程上,阻塞時(shí)無法卸載,影響并發(fā)能力。

解決方案:使用ReentrantLock替代synchronized

// 避免
private final Object lock = new Object();
synchronized (lock) {
    // 阻塞操作...
}

// 推薦
private final Lock lock = new ReentrantLock();
lock.lock();
try {
    // 阻塞操作...
} finally {
    lock.unlock();
}

陷阱二:ThreadLocal的內(nèi)存膨脹風(fēng)險(xiǎn)

由于虛擬線程數(shù)量可能極其龐大,每個(gè)ThreadLocal變量在每個(gè)虛擬線程中都有副本,若存儲(chǔ)大對(duì)象或多變量,累積內(nèi)存開銷不容小覷。

解決方案

  • 優(yōu)先通過方法參數(shù)顯式傳遞上下文

  • 使用不可變對(duì)象(如record)傳遞數(shù)據(jù)

  • 如果必須使用ThreadLocal,務(wù)必在任務(wù)結(jié)束時(shí)調(diào)用remove()清理

5.3 場(chǎng)景適配:虛擬線程不是銀彈

場(chǎng)景類型適用性建議
I/O密集型(Web服務(wù)、API網(wǎng)關(guān)、數(shù)據(jù)庫(kù)訪問)???優(yōu)先使用虛擬線程
混合型負(fù)載??I/O部分用虛擬線程,CPU部分提交到專用平臺(tái)線程池
CPU密集型(計(jì)算、加密、圖像處理)?堅(jiān)持使用傳統(tǒng)固定大小線程池(池大小≈CPU核數(shù))

學(xué)術(shù)研究也證實(shí)了這一判斷:"虛擬線程和平臺(tái)線程各有優(yōu)缺點(diǎn)。不能說虛擬線程絕對(duì)優(yōu)于平臺(tái)線程,這完全取決于工作場(chǎng)景和需要優(yōu)先考慮的指標(biāo)。"

六、可觀測(cè)性:駕馭百萬級(jí)線程

當(dāng)系統(tǒng)運(yùn)行著數(shù)十萬虛擬線程時(shí),傳統(tǒng)監(jiān)控手段可能失效。需要升級(jí)觀測(cè)策略:

6.1 日志與追蹤

確保日志捕獲虛擬線程名稱/ID,并與請(qǐng)求ID、鏈路追蹤ID(如OpenTelemetry)關(guān)聯(lián)。高并發(fā)下快速定位問題全賴于此。

6.2 JMX監(jiān)控指標(biāo)

Tomcat 11等容器已提供專門的虛擬線程監(jiān)控指標(biāo):

  • VirtualThreadsActiveCount:活躍虛擬線程數(shù)

  • VirtualThreadsPeak:峰值虛擬線程數(shù)

  • VirtualThreadsCreated:累計(jì)創(chuàng)建數(shù)

6.3 JFR事件記錄

使用JDK Flight Recorder記錄虛擬線程事件:

jcmd <pid> JFR.start duration=60s filename=virtual_threads.jfr

七、未來展望

虛擬線程的進(jìn)化仍在繼續(xù)。隨著JDK 22及后續(xù)版本對(duì)結(jié)構(gòu)化并發(fā)、作用域值(Scoped Values)等特性的完善,虛擬線程的編程模型將更加友好。作用域值有望成為ThreadLocal的更安全替代方案,專為海量虛擬線程場(chǎng)景設(shè)計(jì)。

對(duì)于開發(fā)者而言,現(xiàn)在就是擁抱虛擬線程的最佳時(shí)機(jī)。它不僅讓Java并發(fā)編程回歸簡(jiǎn)單直觀,更能在不改動(dòng)核心代碼的前提下,為現(xiàn)有系統(tǒng)帶來數(shù)倍的性能提升。正如一位架構(gòu)師所言:"讓線程回歸'廉價(jià)資源'的本質(zhì),開發(fā)者只需專注業(yè)務(wù)邏輯,這才是技術(shù)進(jìn)化的意義。"

到此這篇關(guān)于Java虛擬線程原理與實(shí)踐指南的文章就介紹到這了,更多相關(guān)Java虛擬線程原理與實(shí)踐內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA版最新MyBatis程序配置教程詳解

    IDEA版最新MyBatis程序配置教程詳解

    這篇文章主要介紹了IDEA版最新MyBatis程序配置教程詳解,需要的朋友可以參考下
    2020-07-07
  • Spring-Data-JPA整合MySQL和配置的方法

    Spring-Data-JPA整合MySQL和配置的方法

    這篇文章主要介紹了Spring Data JPA整合MySQL和配置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04
  • java實(shí)現(xiàn)導(dǎo)出Excel的功能

    java實(shí)現(xiàn)導(dǎo)出Excel的功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)導(dǎo)出Excel的功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • 利用java實(shí)現(xiàn)中獎(jiǎng)概率詳情

    利用java實(shí)現(xiàn)中獎(jiǎng)概率詳情

    這篇文章主要介紹了利用java實(shí)現(xiàn)中獎(jiǎng)概率詳情,根據(jù)概率將獎(jiǎng)品劃分區(qū)間,每個(gè)區(qū)間代表一個(gè)獎(jiǎng)品,然后抽取???隨機(jī)數(shù)??,反查落在那個(gè)區(qū)間上,即為所抽取的獎(jiǎng)品,需要的朋友可以參考一下
    2022-07-07
  • 一文讀懂Spring Bean的生命周期

    一文讀懂Spring Bean的生命周期

    今天我們來說一說 Spring Bean 的生命周期,小伙伴們應(yīng)該在面試中經(jīng)常遇到,這是正常現(xiàn)象,本文讓更多的小伙伴們可以輕松的讀懂 Spring Bean 的生命周期
    2023-03-03
  • SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池和監(jiān)控

    SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池和監(jiān)控

    Druid是Java語(yǔ)言中使用的比較多的數(shù)據(jù)庫(kù)連接池。Druid還提供了強(qiáng)大的監(jiān)控和擴(kuò)展功能。面將介紹SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池和監(jiān)控功能,感興趣的可以了解一下
    2021-08-08
  • SpringBoot升級(jí)指定jackson版本的問題

    SpringBoot升級(jí)指定jackson版本的問題

    這篇文章主要介紹了SpringBoot升級(jí)指定jackson版本,本文給大家分享了漏洞通告及修改Springboot中jackson版本的問題,需要的朋友可以參考下
    2022-08-08
  • springboot啟動(dòng)feign項(xiàng)目報(bào)錯(cuò):Service id not legal hostnam的解決

    springboot啟動(dòng)feign項(xiàng)目報(bào)錯(cuò):Service id not legal hostnam的解決

    這篇文章主要介紹了springboot啟動(dòng)feign項(xiàng)目報(bào)錯(cuò):Service id not legal hostnam的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 如何通過java獲取文件名和擴(kuò)展名

    如何通過java獲取文件名和擴(kuò)展名

    這篇文章主要介紹了如何通過java獲取文件名和擴(kuò)展名,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java中異常傳播的實(shí)現(xiàn)

    Java中異常傳播的實(shí)現(xiàn)

    在Java中,異常傳播是一個(gè)重要的概念,本文主要介紹了Java中異常傳播的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01

最新評(píng)論

丽水市| 松滋市| 怀集县| 龙泉市| 舟曲县| 石棉县| 大方县| 五河县| 清丰县| 射洪县| 襄汾县| 高要市| 兴化市| 修水县| 增城市| 三台县| 西充县| 阳高县| 乐亭县| 武山县| 泽普县| 佛冈县| 莱芜市| 林甸县| 明溪县| 东乡族自治县| 沈丘县| 塔城市| 田林县| 芒康县| 安庆市| 东丽区| 宕昌县| 湄潭县| 福安市| 凤翔县| 沛县| 岫岩| 进贤县| 定兴县| 闵行区|