java虛擬線程的超詳細(xì)講解
前言
在 Java 并發(fā)編程的歷史中,線程模型的演進(jìn)一直是性能優(yōu)化的核心。從最初的平臺(tái)線程到如今的虛擬線程,Java 正在經(jīng)歷一場(chǎng)并發(fā)編程的范式轉(zhuǎn)變。
一、傳統(tǒng)線程(平臺(tái)線程):
1.1什么是平臺(tái)線程
平臺(tái)線程是 Java 誕生以來(lái)就存在的線程模型,它與操作系統(tǒng)內(nèi)核線程是一對(duì)一(1:1)映射的關(guān)系:每創(chuàng)建一個(gè) Java 線程,操作系統(tǒng)就會(huì)對(duì)應(yīng)創(chuàng)建一個(gè)內(nèi)核線程,線程的調(diào)度完全由操作系統(tǒng)負(fù)責(zé)。
這種模型的本質(zhì)是 "將 Java 線程直接委托給操作系統(tǒng)管理",優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單,能直接利用操作系統(tǒng)的線程調(diào)度能力,但缺點(diǎn)也同樣顯著。
1.2 平臺(tái)線程的核心痛點(diǎn)
(1)資源消耗巨大
每個(gè)平臺(tái)線程都需要獨(dú)立的??臻g(默認(rèn) 1MB 以上)和內(nèi)核資源,這導(dǎo)致一個(gè)進(jìn)程能創(chuàng)建的線程數(shù)量非常有限。在常規(guī)服務(wù)器上,最多只能創(chuàng)建幾千個(gè)平臺(tái)線程,超過(guò)這個(gè)數(shù)量就會(huì)出現(xiàn) OOM(內(nèi)存溢出)或系統(tǒng)崩潰。
(2)調(diào)度成本高昂
操作系統(tǒng)調(diào)度線程時(shí)需要在用戶態(tài)和內(nèi)核態(tài)之間切換(上下文切換),每次切換耗時(shí)約 1-10 微秒。在高并發(fā)場(chǎng)景下,大量線程的頻繁切換會(huì)嚴(yán)重消耗 CPU 資源,導(dǎo)致 "線程調(diào)度 overhead 超過(guò)實(shí)際任務(wù)執(zhí)行時(shí)間" 的尷尬局面。
(3)并發(fā)編程門(mén)檻高
為了規(guī)避資源限制,開(kāi)發(fā)者必須使用線程池手動(dòng)控制線程數(shù)量,但線程池參數(shù)(核心線程數(shù)、最大線程數(shù)、隊(duì)列大?。┑恼{(diào)優(yōu)極其復(fù)雜,稍有不慎就會(huì)出現(xiàn) "線程耗盡" 或 "資源浪費(fèi)" 的問(wèn)題。
二、虛擬線程:
面對(duì)平臺(tái)線程的局限性,Java 19(2022 年 9 月)正式引入虛擬線程(Virtual Threads),并在 Java 21 中成為穩(wěn)定特性。它不是對(duì)平臺(tái)線程的修補(bǔ),而是一種全新的線程模型。
2.1什么是虛擬線程
虛擬線程是由 JVM 管理的用戶態(tài)線程,它不直接映射到操作系統(tǒng)內(nèi)核線程,而是通過(guò) "多對(duì)多(M:N)" 的方式映射到少量平臺(tái)線程上:多個(gè)虛擬線程可以共享一個(gè)平臺(tái)線程,由 JVM 負(fù)責(zé)在用戶態(tài)完成調(diào)度。
簡(jiǎn)單說(shuō),虛擬線程就像是 "JVM 級(jí)別的輕量級(jí)線程",它的創(chuàng)建、銷(xiāo)毀和調(diào)度完全由 JVM 控制,無(wú)需操作系統(tǒng)介入。
2.2虛擬線程的核心優(yōu)勢(shì)
(1)極致輕量化
- 初始棧大小僅幾百字節(jié)(平臺(tái)線程是 MB 級(jí))
- 支持創(chuàng)建百萬(wàn)級(jí)甚至千萬(wàn)級(jí)線程(平臺(tái)線程僅支持幾千級(jí))
- 線程創(chuàng)建成本降低 99% 以上
(2)智能調(diào)度機(jī)制
當(dāng)虛擬線程執(zhí)行阻塞操作(如網(wǎng)絡(luò) IO、文件讀寫(xiě))時(shí),JVM 會(huì)自動(dòng)將其 "掛起",并將底層平臺(tái)線程讓給其他虛擬線程使用。這種 "阻塞即讓出" 的特性,徹底解決了傳統(tǒng)線程 "阻塞時(shí)浪費(fèi)資源" 的問(wèn)題。
(3)兼容現(xiàn)有代碼
虛擬線程完全兼容Thread、Runnable、ExecutorService等現(xiàn)有并發(fā) API,無(wú)需學(xué)習(xí)新框架就能上手,這意味著你可以用同步代碼的寫(xiě)法,實(shí)現(xiàn)異步代碼的性能。
對(duì)比
特性 | 平臺(tái)線程(普通線程) | 虛擬線程(Virtual Threads) |
底層映射關(guān)系 | 1:1(Java 線程→操作系統(tǒng)線程) | M:N(多個(gè)虛擬線程→少量操作系統(tǒng)線程) |
資源占用 | 高(棧內(nèi)存固定 1MB+) | 低(棧內(nèi)存動(dòng)態(tài)分配,初始僅幾百字節(jié)) |
最大并發(fā)支持 | 幾千個(gè)線程 | 百萬(wàn)級(jí)線程 |
調(diào)度方 | 操作系統(tǒng)內(nèi)核(內(nèi)核態(tài)調(diào)度) | JVM(用戶態(tài)調(diào)度) |
上下文切換成本 | 高(約 1-10 微秒,需內(nèi)核態(tài)切換) | 低(約 0.1 微秒,用戶態(tài)內(nèi)完成) |
阻塞時(shí)資源利用率 | 低(線程阻塞時(shí)仍占用操作系統(tǒng)線程) | 高(阻塞時(shí)自動(dòng)釋放底層線程資源) |
適用場(chǎng)景 | 計(jì)算密集型任務(wù) | IO 密集型任務(wù)(網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)、文件 IO) |
創(chuàng)建成本 | 高(需操作系統(tǒng)調(diào)用) | 極低(JVM 直接創(chuàng)建) |
代碼測(cè)試
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadComparisonDemo {
private static final int TASK_COUNT = 10000; // 任務(wù)數(shù)量
private static final int IO_DELAY = 10; // 模擬IO延遲(毫秒)
public static void main(String[] args) throws InterruptedException {
System.out.println("任務(wù)數(shù)量: " + TASK_COUNT + ", IO延遲: " + IO_DELAY + "ms");
// 測(cè)試普通線程
long platformTime = measureTime(() -> runWithPlatformThreads());
System.out.printf("普通線程耗時(shí): %d ms%n", platformTime);
// 測(cè)試虛擬線程
long virtualTime = measureTime(() -> runWithVirtualThreads());
System.out.printf("虛擬線程耗時(shí): %d ms%n", virtualTime);
// 計(jì)算性能提升百分比
double improvement = (1.0 - (double) virtualTime / platformTime) * 100;
System.out.printf("虛擬線程性能提升: %.2f%%%n", improvement);
}
// 普通線程測(cè)試
private static void runWithPlatformThreads() throws InterruptedException {
try (ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())) {
for (int i = 0; i < TASK_COUNT; i++) {
executor.submit(() -> simulateIoTask());
}
}
}
// 虛擬線程測(cè)試
private static void runWithVirtualThreads() throws InterruptedException {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < TASK_COUNT; i++) {
executor.submit(() -> simulateIoTask());
}
}
}
// 模擬IO密集型任務(wù)
private static void simulateIoTask() {
try {
Thread.sleep(IO_DELAY); // 模擬IO等待時(shí)間
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 計(jì)時(shí)輔助方法
private static long measureTime(Runnable action) {
Instant start = Instant.now();
try {
action.run();
} catch (Exception e) {
e.printStackTrace();
}
return Duration.between(start, Instant.now()).toMillis();
}
} 總結(jié)
到此這篇關(guān)于java虛擬線程的文章就介紹到這了,更多相關(guān)java虛擬線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何使用Jackson和JSON Pointer查詢(xún)解析任何JSON節(jié)點(diǎn)
本文介紹了JSON Pointer是字符串表達(dá)式,可以非常方便解析復(fù)雜JSON節(jié)點(diǎn)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
springboot整合mybatis-plus實(shí)現(xiàn)多表分頁(yè)查詢(xún)的示例代碼
這篇文章主要介紹了springboot整合mybatis-plus實(shí)現(xiàn)多表分頁(yè)查詢(xún)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
springboot配合Thymeleaf完美實(shí)現(xiàn)遍歷功能
Thymeleaf顯然是一個(gè)開(kāi)發(fā)頁(yè)面的技術(shù),現(xiàn)在各種前端技術(shù)層出不窮,比如現(xiàn)在主流的Vue、React、AngularJS等。這篇文章主要介紹了springboot配合Thymeleaf完美實(shí)現(xiàn)遍歷,需要的朋友可以參考下2021-09-09
eclipse中自動(dòng)生成javadoc文檔的方法
這篇文章主要介紹了eclipse中自動(dòng)生成javadoc文檔的方法,是實(shí)用eclipse開(kāi)發(fā)Java程序時(shí)非常實(shí)用的技巧,對(duì)于進(jìn)行Java項(xiàng)目開(kāi)發(fā)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
SQLSyntaxErrorException-ExecutorException報(bào)錯(cuò)解決分析
這篇文章主要為大家介紹了SQLSyntaxErrorException-ExecutorException報(bào)錯(cuò)解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
mybatis動(dòng)態(tài)sql之Map參數(shù)的講解
今天小編就為大家分享一篇關(guān)于mybatis動(dòng)態(tài)sql之Map參數(shù)的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03

