JDK21 發布已經過去1個月時間了,除了每次發版必然更新的GC機制,和一些增強功能外,還引入了一個全新的概念——虛擬線程。
什么是虛擬線程
先來看一下官方對虛擬線程(Visual Threads)的描述(已翻譯):
如今,JDK中JAVA.lang.Thread的每個實例都是一個平臺線程。平臺線程在底層操作系統線程上運行Java代碼,并在代碼的整個生命周期中捕獲操作系統線程。平臺線程的數量限制為操作系統線程的數量。
虛擬線程是java.lang.thread的一個實例,它在底層操作系統線程上運行java代碼,但在代碼的整個生命周期內不會捕獲該操作系統線程。這意味著許多虛擬線程可以在同一個操作系統線程上運行Java代碼,從而有效地共享代碼。
雖然平臺線程獨占了寶貴的操作系統線程,但虛擬線程卻沒有。虛擬線程的數量可以比操作系統線程的數量大得多。
虛擬線程是JDK而不是OS提供的線程的輕量級實現。它們是用戶模式線程的一種形式,在其他多線程語言中也很成功(例如Go中的goroutines和Erlang中的processes)。用戶模式線程甚至在早期版本的Java中被稱為“綠色線程”,當時操作系統線程還不成熟和廣泛使用。然而,Java的綠色線程都共享一個操作系統線程(M:1調度),并且最終被平臺線程所超越,后者被實現為操作系統線程的包裝器(1:1調度)。虛擬線程采用M:N調度,其中大量(M)虛擬線程被調度在少量(N)OS線程上運行。
簡而言之,現在我們可以輕松地創建一個輕量級的虛擬線程,實現簡單性,同時充分發揮硬件性能。
可以概括為: 輕量、多路復用、開銷極小、異步編程、可移植性
測試
開始一個簡單的測試:1萬并發阻塞1秒模仿web連接池,使用Thread.sleep(1) 模擬IO操作需要的時間。
public static void mAIn(String[] args) throws InterruptedException {
long l = System.currentTimeMillis();
// normal();
virtual();
System.out.println(System.currentTimeMillis() - l);
}
先使用常規線程池方式運行:
public static void normal() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(200);
for (int i=0; i < 10000; i++) {
executor.execute(() -> {
try {
System.out.println("normal");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(100, java.util.concurrent.TimeUnit.SECONDS);
}
得到如下結果:
圖片
使用 JDK21 虛擬線程池運行:
public static void virtual() throws InterruptedException {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i=0; i < 10000; i++) {
executor.execute(() -> {
try {
System.out.println("jdk21");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(100, java.util.concurrent.TimeUnit.SECONDS);
}
得到如下結果:
圖片
20多倍的提速?。?!
我不理解,但我大受震撼!??!
如果有朋友想自己測試一下虛擬線程,或者JDK21的其他新特性,可以嘗試一下哦~
JDK21 官網下載鏈接:https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_windows-x64_bin.zip
目前虛擬線程還屬于預覽版本,沒有正式啟用。如果是IDEA 開發工具,在啟動的時候,需要配置VM參數 --enable-preview
圖片
小結
經過測試,在該例子中,Executors.newFixedThreadPool( size ); size的值越大,普通的線程池用時越短,在線程池的大小開放到 10000后,用時幾乎一樣。
應該是本例的IO操作是Thread.sleep的緣故,所以不會占用CPU資源。
傳統的線程池,一般我們會指定核心線程數量為 CPU核心數*4,最大線程數為 CPU核心數*8,而在虛擬線程池中,不需要指定線程數,JDK會自動根據資源占用情況,創建合適數量的線程,追求更高的吞吐量。
聽說springboot3.2.0版本開始,springboot要把webflux底層也改成虛擬線程了,不知道以后會不會把所有的關于異步線程的地方都改成虛擬線程。
了不起是覺得這個虛擬線程真是個好東西,簡單易用,再也不糾結怎么配置線程池,根據硬件自適應,效率極高。
現在Spring6是基于JDK17的,不知道下個大版本更新會不會直接更新到有虛擬線程穩定發布的JDK版本,期待!