Java21新特性——虛擬線程
Java21最重要的新特性之一是虛擬線程(Virtual Threads)。 傳統的Java線程受制于操作系統的線程數,并發能力和可伸縮性有限,許多時候資源無法充分利用。而虛擬線程則提供了一種更高效、更輕量級的線程模型。虛擬線程,也稱為“用戶模式線程(user-mode threads)”或“纖程(fibers)”。該功能旨在簡化并發編程并提供更好的可擴展性。虛擬線程是輕量級的,它們可以比傳統線程創建更多數量,并且開銷要少得多。
本文主要介紹Java傳統的線程和虛擬線程的特點和區別,以及虛擬線程的編碼方法和注意事項。
傳統的線程
在舊的Java版本中使用的線程依賴于操作系統的線程,創建線程、銷毀線程以及線程切換都需要大量性能開銷。而操作系統的線程數有限,當應用系統需要大量線程的時候,可能會導致系統資源耗竭,性能下降,甚至導致系統奔潰。在舊的Java版本中,我們所使用java.lang.Thread來定義線程,這個就是由操作系統所支持的線程。這種線程通常以1:1的比例映射到OS調度的內核。OS線程相當“重”。根據操作系統配置,默認情況下,每個線程消耗2到10 MB, 因此,如果想在發應用程序中使用一百萬個線程,那么就要求有超過2TB的內存可供使用!很明顯,這就限制了線程數量。
在基于Java的Web應用中,每個請求使用一個線程有很多優點,比如狀態管理和清理更加容易。但它也造成了可擴展性的限制。容易使CPU或網絡資源耗盡。
虛擬線程
Java21引入虛擬線程,使得Java應用程序的線程不再受制于操作系統,可以在應用中創建多達數十億的線程,更好地適應各種高并發場景,提供更高的并發能力。虛擬線程具有以下優點:
- 更高的性能:虛擬線程不再受制于操作系統的線程數,并且減少了線程創建、銷毀、共享等操作的性能開銷。從而獲得更高的并發性能。
- 更高可伸縮性:虛擬線程可以創建多達數十億的線程,更能適應Java應用的大規模并發場景。
- 資源消耗更低:虛擬線程比操作系統的線程更加輕量級,資源利用率較高,CPU和內存占用較少。
虛擬線程是一個java.lang.Thread變體,是Project Loom的一部分,不受操作系統的管理或調度,而是由JVM負責調度。當然,任何底層的邏輯都還必須在操作系統線程中運行,只是JVM利用載體線程(carrier threads,也就是平臺線程)之上“攜帶”虛擬線程。
編碼示例
虛擬線程的學習成本比較低,只需要像對待非虛擬線程一樣對待他們就可以了。
(1) 傳統線程的開發傳統線程的用法在使用虛擬線程之前我們先來回顧一下傳統的線程的寫法。
Runnable fn = () -> {
// 業務代碼
};
Thread thread = new Thread(fn).start();
Project Loom 簡化了并發方法,它提供了一種新方法來創建平臺的線程:
Thread thread = Thread.ofPlatform().
.start(runnable);
或者:
Thread thread = Thread.ofPlatform().
.daemon()
.name("my-custom-thread")
(2) 虛擬線程的用法
API寫法:
Runnable fn = () -> {
// 業務代碼
};
Thread thread = Thread.ofVirtual(fn)
.start();
Project Loom 寫法:
Thread thread = Thread.startVirtualThread(() -> {
// 業務代碼
});
創建虛擬線程的另一種方法是使用Executor:
var executorService = Executors.newVirtualThreadPerTaskExecutor();
executorService.submit(() -> {
// 業務代碼
});
因為所有的虛擬線程都是守護線程,所以如果想在主線程上等待,就需要調用join()方法,Join方法的作用就是讓主線程等待,當有新的線程加入時,主線程會進入等待狀態,一直到調用方法的副線程執行結束為止。
thread.join();
虛擬線程開發注意事項
- 注意控制線程數:虛擬線程可以創建大量線程,很容易讓開發人員不在意其數量,而過多的線程仍然會導致性能下降或資源耗盡。因此,仍需根據資源數量合理控制應用程序的并發度。
- 注意線程安全:使用虛擬線程時要注意線程安全性和正確性,避免共享可變狀態、根據需要使用同步機制。
- 注意代碼遷移:在從傳統線程遷移到使用虛擬線程的時候,需要注意代碼與新環境、新規范、新需求的一致性。
總結
虛擬線程是Java并發開發方面的通用、強大的新方法,在Java21版本中已經十分成熟了。對于需要從舊版本JDK遷移到新版本JDK的應用程序來說,改造難度并不大,同時還可以充分利用所有可用硬件資源,提高Java應用程序的并發性和可伸縮性。