JVM 是一個多線程環(huán)境,它通過 java.lang.Thread 提供了對操作系統(tǒng)線程的抽象,但是Java中的線程都只是對操作系統(tǒng)線程的一種簡單封裝,可以稱之為“平臺線程”。在某些場景下,它是有一些缺陷的,具體體現(xiàn)在:
代價昂貴:創(chuàng)建平臺線程的成本很高。每當創(chuàng)建一個平臺線程時,操作系統(tǒng)都必須在堆棧中分配大量內(nèi)存來存儲線程的上下文、原生調(diào)用堆棧和Java調(diào)用堆棧。
上下文切換成本高:在多線程環(huán)境下,需要在不同線程間切換,這種上下文切換會消耗時間和資源。
線程數(shù)量有限:Java 線程僅僅只是對操作系統(tǒng)線程的封裝,而操作系統(tǒng)線程的數(shù)量是有限的,這就限制了Java 同時運行的線程數(shù)量,從而限制了應用程序的并發(fā)能力。
虛擬線程則是通過平臺線程(載體線程)來管理,一個平臺線程可以在不同的時間執(zhí)行不同的虛擬線程(多個虛擬線程掛載在一個平臺線程上),當虛擬線程被阻塞或等待時,平臺線程可以切換到執(zhí)行另一個虛擬線程。
優(yōu)點
非常輕量級:可以在單個線程中創(chuàng)建成百上千個虛擬線程而不會導致過多的線程創(chuàng)建和上下文切換。
簡化異步編程:虛擬線程可以簡化異步編程,使代碼更易于理解和維護。它可以將異步代碼編寫得更像同步代碼,避免了回調(diào)地獄。
減少資源開銷:由于虛擬線程是由 JVM 實現(xiàn)的,它能夠更高效地利用底層資源,例如 CPU 和內(nèi)存。虛擬線程的上下文切換比平臺線程更輕量,因此能夠更好地支持高并發(fā)場景。
提升性能:在I/O 密集型應用中,虛擬線程能夠顯著地提升性能。而且由于它們的創(chuàng)建和銷毀成本低,能夠更加高效地利用系統(tǒng)資源。
虛擬線程并不旨在完全替代傳統(tǒng)的操作系統(tǒng)線程,而是作為一個補充。對于需要密集計算和精細控制線程行為的場景,傳統(tǒng)線程仍然是主流。
虛擬線程主要針對高并發(fā)和高吞吐量,而不是最低延遲。對于需要極低延遲的應用,傳統(tǒng)線程可能是更好的選擇。
虛擬線程改進了線程的實現(xiàn)方式,但并未改變Java基本的線程模型和同步機制。鎖和同步仍然是并發(fā)控制的重要工具。
官方提供了以下四種方式創(chuàng)建虛擬線程:
(1)Thread.startVirtualThread()是Thread提供的靜態(tài)方法,創(chuàng)建一個新的虛擬線程,并立即執(zhí)行給定的Runnable任務
@Test
public void virtualThreadTest() {
Thread.startVirtualThread(() -> {
// 這里放置你的任務代碼
System.out.println("虛擬線程");
});
???????}
(2)Thread.ofVirtual 是一個流式API,用于構(gòu)建和配置線程。它提供了設置線程屬性(如名稱、守護狀態(tài)、優(yōu)先級、未捕獲異常處理器等)的方法。相比直接使用 Thread 來構(gòu)建線程,Thread.Builder提供了更多的靈活性和控制力。
@Test
???????public void virtualThreadTest(){
Thread.ofVirtual()
.name("virtualThreadTest")
.uncaughtExceptionHandler((t,e)-> System.out.println("線程[" + t.getName() + "發(fā)生了異常。message:" + e.getMessage()))
???????.start(()->{System.out.println("虛擬線程");
});
???????}
(3)ThreadFactory
public class VirtualThreadTest {
public static void main(String[] args) {
CustomThread customThread = new CustomThread();
ThreadFactory factory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(customThread);
???????thread.start();
}
}
static class CustomThread implements Runnable{
@Override
public void run() {
System.out.println("CustomThread run");
}
???????}
(4)Executors.newVirtualThreadPerTaskExecutor() 虛擬線程也支持線程池,線程池會給每個任務分配一個虛擬線程
public class VirtualThreadTest {
public static void main(String[] args) {
CustomThread customThread = new CustomThread();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(customThread);
}
}
static class CustomThread implements Runnable {
@Override
public void run() {
System.out.println("CustomThread run");
}
???????}
虛擬線程是由JVM調(diào)度的,而不是操作系統(tǒng)。它們占用空間小,同時使用輕量級的任務隊列來調(diào)度,避免了線程間基于內(nèi)核的上下文切換開銷。在密集 IO 的場景下,需要創(chuàng)建大量的平臺線程異步處理才能達到虛擬線程的處理速度。因此,在密集 IO 的場景,虛擬線程可以大幅提高線程的執(zhí)行效率,減少線程資源的創(chuàng)建以及上下文切換。雖然虛擬線程在資源的消耗上面比平臺線程少,但是仍然需要合理管理,創(chuàng)建過多的虛擬線程可能會導致內(nèi)存消耗的增加。通常我們不需要直接使用虛擬線程,像Tomcat、Netty、Spring boot等都已經(jīng)支持虛擬線程。