網(wǎng)易面試:SpringBoot如何開啟虛擬線程?
虛擬線程(Virtual Thread)也稱協(xié)程或纖程,是一種輕量級的線程實現(xiàn),與傳統(tǒng)的線程以及操作系統(tǒng)級別的線程(也稱為平臺線程)相比,它的創(chuàng)建開銷更小、資源利用率更高,是 Java 并發(fā)編程領(lǐng)域的一項重要創(chuàng)新。
PS:虛擬線程正式發(fā)布于 Java 長期支持版(Long Term Suort,LTS)Java 21(也就是 JDK 21)。
虛擬線程是一種在 Java 虛擬機(jī)(JVM)層面實現(xiàn)的邏輯線程,不直接和操作系統(tǒng)的物理線程一一對應(yīng),因此它可以減少上下文切換所帶來的性能開銷。
操作系統(tǒng)線程、普通線程(Java 線程)和虛擬線程的關(guān)系如下:
1.虛擬線程使用
虛擬線程的創(chuàng)建有以下 4 種方式:
- Thread.startVirtualThread(Runnable task)
- Thread.ofVirtual().unstarted(Runnable task)
- Thread.ofVirtual().factory()
- Executors.newVirtualThreadPerTaskExecutor()
具體使用如下。
(1)startVirtualThread
創(chuàng)建虛擬線程,并直接啟動執(zhí)行任務(wù):
// 創(chuàng)建并啟動虛擬線程
Thread.startVirtualThread(() -> {
System.out.println("Do virtual thread.");
});
(2)unstarted
只創(chuàng)建虛擬線程,但不直接啟動(創(chuàng)建之后通過 start 啟動):
// 創(chuàng)建虛擬線程
Thread vt = Thread.ofVirtual().unstarted(()->{
System.out.println("Do virtual thread.");
});
// 運行虛擬線程
vt.start();
(3)factory
先創(chuàng)建虛擬線程工廠,然后再使用工廠創(chuàng)建虛擬線程,之后再調(diào)用 start() 方法進(jìn)行執(zhí)行:
// 創(chuàng)建虛擬線程工廠
ThreadFactory tf = Thread.ofVirtual().factory();
// 創(chuàng)建虛擬線程
Thread vt = tf.newThread(()->{
System.out.println("Do virtual thread.");
});
// 運行虛擬線程
vt.start();
(4)newVirtualThreadPerTaskExecutor
創(chuàng)建虛擬線程池:
// 創(chuàng)建一個支持虛擬線程的線程池
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(()->{
System.out.println("Do virtual thread.");
});
2.虛擬線程 VS 普通線程
虛擬線程和普通線程的區(qū)別主要體現(xiàn)在以下幾點:
- 普通線程是和操作系統(tǒng)的物理線程是一一對應(yīng)的,而虛擬線程是 JVM 層面的邏輯線程,并不和操作系統(tǒng)的物理線程一一對應(yīng),它可以看作是輕量級的線程。
- 普通線程默認(rèn)創(chuàng)建的是用戶線程(而守護(hù)線程),而虛擬線程是守護(hù)線程,并且其守護(hù)線程的屬性不能被修改,如果修改就會報錯,如下圖所示:
- 虛擬線程由 JVM 調(diào)度和使用,避免了普通線程頻繁切換的性能開銷,所以相比于普通的線程來說,運行效率更高。
3.SpringBoot開啟虛擬線程
以最新版的 Spring Boot 3.x 為例,我們開啟虛擬線程很簡單,只需要在 Spring Boot 配置文件中設(shè)置“spring.threads.virtual.enabled”為“true”即可開啟,以 application.yml 為例,啟用虛擬線程配置如下:
spring:
threads:
virtual:
enabled: true # 啟用虛擬線程
這樣 Spinrg Boot 在啟動 Tomcat 容器時,會使用一個虛擬線程執(zhí)行器來代表原有的平臺線程池。
PS:這里是虛擬線程執(zhí)行器,不是虛擬線程池。
如果以上配置未生效的話,還可以通過修改 Tomcat 配置類,讓其使用虛擬線程來處理每一個請求,配置代碼如下:
import java.util.concurrent.Executors;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TomcatConfiguration {
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
// 使用虛擬線程來處理每一個請求
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}
4.異步任務(wù)開啟虛擬線程
如果你想為 Spring Boot 中的異步任務(wù) @Async 也配置虛擬線程的話,可以在 AsyncConfigurer 配置類中設(shè)置,配置代碼如下:
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync // 開啟異步任務(wù)
public class AsyncTaskConfiguration implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new TaskExecutorAdapter(Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("virtual-async#", 1).factory()));
}
}