一文詳解JDK21中虛擬線程
JEP 425: Virtual Threads (Preview) 虛擬線程,輕量級(jí)的線程模型對(duì)標(biāo)其他語(yǔ)言中的協(xié)程,能夠顯著的減少編寫、維護(hù)和觀察高并發(fā)應(yīng)用程序的工作量。
該特性的目標(biāo):
- 支持服務(wù)端應(yīng)用程序以thread-per-request樣式編寫,并最大限度壓榨硬件性能。
- 兼容java.lang.Thread API,減少調(diào)用方代碼改動(dòng)。
- 兼容現(xiàn)有的JDK工具,用于故障排查、調(diào)整和分析。
- 不會(huì)替代線程的傳統(tǒng)實(shí)現(xiàn),也不會(huì)靜默升級(jí)現(xiàn)有的線程模型
- 不會(huì)改變Java的并發(fā)編程模型
- 在Java語(yǔ)言或Java庫(kù)中提供新的數(shù)據(jù)并行結(jié)構(gòu),不會(huì)動(dòng)搖Stream API的地位。
創(chuàng)建虛擬線程
虛擬線程并沒(méi)有引入獨(dú)立的API,而是選擇兼容Thread的API。這樣已有應(yīng)用程序只需要做最小的改動(dòng)就能使用協(xié)程帶來(lái)的好處。
Thread thread = Thread.ofVirtual().name("duke").unstarted(runnable);
Thread.startVirtualThread(Runnable)
Thread.isVirtual()為了支持虛擬線程,Thread API被稍微修改,ThreadGroup不再支持
虛擬線程vs平臺(tái)線程
虛擬線程適合大量任務(wù)數(shù),低CPU負(fù)載的任務(wù)。如果是CPU為瓶頸的應(yīng)用,使用協(xié)程不會(huì)有性能改進(jìn)。
虛擬線程的調(diào)度
Java虛擬線程采用M:N模式,即M個(gè)協(xié)程在N個(gè)線程中運(yùn)行。調(diào)度線程默認(rèn)和核心數(shù)一樣多。可以通過(guò) system property jdk.virtualThreadScheduler.parallelism來(lái)配置。
虛擬線程的載體線程不是固定的,在生命其中可能運(yùn)行在不同的載體線程中。載體(carrier)線程和虛擬線程的堆棧信息是分離的,不會(huì)相互影響。
ThreadLocal和currentThread獲取的都是獨(dú)立的。在java代碼曾來(lái)說(shuō)虛擬線程后面的系統(tǒng)線程是不可見(jiàn)的。
虛擬線程的執(zhí)行
虛擬線程會(huì)在IO阻塞或者其他等待的時(shí)候掛起,在可用的時(shí)候恢復(fù)執(zhí)行。這不會(huì)阻塞載體線程,載體線程會(huì)去運(yùn)行新任務(wù)。受限于系統(tǒng),個(gè)別的阻塞操作可能也會(huì)阻塞載體線程,這時(shí)候可能會(huì)臨時(shí)擴(kuò)大調(diào)度池的大小。
一些情況下會(huì)釘?。╬inned)載體線程,當(dāng)同步方法/塊或者當(dāng)執(zhí)行本地/外部函數(shù)時(shí)。被釘住的虛擬線程在執(zhí)行很多操作的時(shí)候會(huì)阻塞背后的系統(tǒng)線程。高頻或者長(zhǎng)時(shí)間的synchronized使用java.util.concurrent.locks.ReentrantLock來(lái)代替,可以減少釘住。
JDK Flight Recorder可以查看被釘住的線程。-Djdk.tracePinnedThreads=full參數(shù)將能獲取到完整的釘住線程的堆棧信息。將來(lái)可能會(huì)改進(jìn)pinning inside synchronized的情況。
內(nèi)存使用和垃圾收集器
虛擬線程的堆棧分配在垃圾收集的堆上。棧在運(yùn)行時(shí)的增長(zhǎng)和縮減,所以占用的內(nèi)存少,非常的高效。
虛擬線程不是垃圾收集的根,不再使用的虛擬線程將會(huì)被回收掉。
由于虛擬線程通常比較多,Thread-local開(kāi)銷就會(huì)比較大,需要謹(jǐn)慎使用。
已有應(yīng)用遷移到虛擬線程
只需要三步:
1.將普通線程的創(chuàng)建改成創(chuàng)建虛擬線程。
2.取消池化機(jī)制。因?yàn)樘摂M線程非常輕量級(jí),不需要池化。
3.synchronized改為ReentrantLock,以減少被釘住。被釘住的虛擬線程容易阻塞背后的載體線程。
和async/await的比較
async/await比有棧協(xié)程在某些情況下更好用。例如請(qǐng)求c需要a和b作為參數(shù)的情況下,async/await比有棧協(xié)程實(shí)現(xiàn)起來(lái)更加簡(jiǎn)單直接:
a = requestA() b = requestB() c = await request(await a, await b)
例
寫一個(gè)簡(jiǎn)單的例子,開(kāi)n個(gè)虛擬線程,每個(gè)線程sleep 1秒后累加1次,以模擬IO密集型操作。
private static void runVirtualThread(int length) {
AtomicInteger ai = new AtomicInteger();
long start = System.currentTimeMillis();
try (ExecutorService es = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < length; i++) {
es.submit(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ai.incrementAndGet() >= length) {
System.out.printf("duration=%d ms, done.%n", System.currentTimeMillis() - start);
}
});
}
}
}然后再使用ScheduledExecutorService配合JMXBean打印JVM線程狀態(tài)
ScheduledExecutorService se = Executors.newScheduledThreadPool(1);
se.scheduleAtFixedRate(() -> {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false);
System.out.printf("threadNumber=%d%n", threadInfo.length);
}, 100, 1000, TimeUnit.MILLISECONDS);
runVirtualThread(100000);
// runThread(100000);
Thread.sleep(100 * 1000);
se.shutdownNow();再寫一個(gè)使用傳統(tǒng)操作系統(tǒng)線程的對(duì)比程序
private static void runThread(int length) {
AtomicInteger ai = new AtomicInteger();
long start = System.currentTimeMillis();
try (ExecutorService es = Executors.newCachedThreadPool()) {
for (int i = 0; i < length; i++) {
es.submit(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ai.incrementAndGet() >= length) {
System.out.printf("duration=%d ms, done.%n", System.currentTimeMillis() - start);
}
});
}
}
}執(zhí)行
分別執(zhí)行虛擬線程和操作系統(tǒng)線程,入?yún)ength為100,000,執(zhí)行環(huán)境為Win11,Intel i5-12600KF。
- 使用虛擬線程時(shí)輸出如下:
threadNumber=26
threadNumber=26
threadNumber=26
threadNumber=26
duration=3530 ms, done.
- 操作系統(tǒng)線程時(shí)輸出如下:
threadNumber=2613
threadNumber=11072
threadNumber=15925
threadNumber=19725
threadNumber=22336
threadNumber=23933
duration=11614 ms, done.
結(jié)論
分別執(zhí)行10次并刪除壞點(diǎn)后,執(zhí)行結(jié)果如下:
| 線程類型 | 執(zhí)行時(shí)間(ms) | JVM線程數(shù)量 | 打開(kāi)句柄數(shù)量 |
|---|---|---|---|
| 虛擬線程 | 3,400 | 26 | 600 |
| 操作系統(tǒng)線程 | 11,500 | 23000 | 124,000 |
如上表所示,虛擬線程在執(zhí)行IO密集型高并發(fā)任務(wù)時(shí),性能遠(yuǎn)優(yōu)于操作系統(tǒng)線程方式,對(duì)于操作系統(tǒng)資源的占用也降低很多.
到此這篇關(guān)于一文詳解JDK21中虛擬線程的文章就介紹到這了,更多相關(guān)JDK21虛擬線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot?中的?Filter?實(shí)現(xiàn)超大響應(yīng)?JSON?數(shù)據(jù)壓縮的方法
這篇文章主要介紹了Springboot?中的?Filter?實(shí)現(xiàn)超大響應(yīng)?JSON?數(shù)據(jù)壓縮,定義GzipFilter對(duì)輸出進(jìn)行攔截,定義 Controller該 Controller 非常簡(jiǎn)單,主要讀取一個(gè)大文本文件,作為輸出的內(nèi)容,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10
java實(shí)現(xiàn)簡(jiǎn)單的圖書管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
java數(shù)據(jù)結(jié)構(gòu)與算法之noDups去除重復(fù)項(xiàng)算法示例
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)與算法之noDups去除重復(fù)項(xiàng)算法實(shí)現(xiàn)技巧,程序代碼非常簡(jiǎn)單,關(guān)鍵在于循環(huán)與判定,需要的朋友可以參考下2016-08-08
java 將jsonarray 轉(zhuǎn)化為對(duì)應(yīng)鍵值的jsonobject方法
下面小編就為大家分享一篇java 將jsonarray 轉(zhuǎn)化為對(duì)應(yīng)鍵值的jsonobject方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
SpringBoot中整合MyBatis-Plus的方法示例
這篇文章主要介紹了SpringBoot中整合MyBatis-Plus的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決
這篇文章主要介紹了springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
SpringCloud的Gateway網(wǎng)關(guān)詳解
這篇文章主要介紹了SpringCloud的Gateway網(wǎng)關(guān)詳解,Gateway 是 Spring Cloud 官方推出的一個(gè)基于 Spring 5、Spring Boot 2 和 Project Reactor 的 API 網(wǎng)關(guān)實(shí)現(xiàn),本文將介紹 Spring Cloud Gateway 的基本概念、核心組件以及如何配置和使用它,需要的朋友可以參考下2023-09-09
springboot + vue 實(shí)現(xiàn)遞歸生成多級(jí)菜單(實(shí)例代碼)
這篇文章主要介紹了springboot + vue 實(shí)現(xiàn)遞歸生成多級(jí)菜單,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12

