虚拟线程(Virtual Threads)简介
虚拟线程是 JDK 19 引入的预览特性,并在 JDK 21 中成为正式特性。它是 Java 平台线程模型的一次重大改进,旨在解决传统线程在高并发场景下的性能和资源限制问题。
虚拟线程与普通线程的区别
| 特性 | 普通线程(Platform Threads) | 虚拟线程(Virtual Threads) |
|---|---|---|
| 实现方式 | 映射到操作系统线程(OS Thread) | 由 JVM 管理,轻量级线程 |
| 线程数量限制 | 受限于操作系统资源(如内存、线程数) | 几乎无限制,支持数百万级线程 |
| 阻塞操作 | 阻塞操作会占用操作系统线程 | 阻塞操作会挂起虚拟线程,不占用 OS 线程 |
| 上下文切换 | 依赖操作系统,开销较大 | JVM 内部管理,开销更小 |
| 适用场景 | 适合中小规模并发 | 适合大规模并发(如高并发 Web 服务) |
| 线程栈 | 每个线程分配固定大小的内存栈 | 栈按需分配,内存利用率更高 |
| 调试和监控 | 使用传统工具(如 JStack) | 与普通线程一致,工具兼容性好 |
虚拟线程的好处
-
高并发性能:
- 虚拟线程是轻量级的,可以在 JVM 中创建数百万个线程,而不会耗尽系统资源。
- 适合高并发场景,如 Web 服务、微服务和实时数据处理。
-
简化异步编程:
- 虚拟线程支持阻塞式编程模型,开发者可以直接使用同步代码风格,而不需要复杂的异步回调或
CompletableFuture。
- 虚拟线程支持阻塞式编程模型,开发者可以直接使用同步代码风格,而不需要复杂的异步回调或
-
更高的资源利用率:
- 虚拟线程的栈按需分配,内存利用率更高。
- 阻塞操作不会占用操作系统线程,释放了更多资源。
-
与现有工具兼容:
- 虚拟线程与普通线程共享相同的调试和监控工具(如 JStack、JVisualVM)。
代码对比:普通线程 vs 虚拟线程
1. 使用普通线程实现高并发
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PlatformThreadsExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10); // 固定线程池
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
System.out.println("Running in platform thread: " + Thread.currentThread());
try {
Thread.sleep(1000); // 模拟阻塞操作
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
问题:
- 线程池大小有限(这里是 10),无法同时处理大量任务。
- 阻塞操作(如
Thread.sleep)会占用操作系统线程,降低资源利用率。
2. 使用虚拟线程实现高并发
import java.util.concurrent.Executors;
public class VirtualThreadsExample {
public static void main(String[] args) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { // 虚拟线程池
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
System.out.println("Running in virtual thread: " + Thread.currentThread());
try {
Thread.sleep(1000); // 模拟阻塞操作
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
} // 自动关闭虚拟线程池
}
}
优势:
- 每个任务都运行在独立的虚拟线程中,支持数百万级并发。
- 阻塞操作(如
Thread.sleep)不会占用操作系统线程,资源利用率更高。
虚拟线程的使用案例
1. 高并发 HTTP 服务器
使用虚拟线程处理每个 HTTP 请求:
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
public class VirtualThreadHttpServer {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", exchange -> {
try (exchange) {
String response = "Hello, Virtual Threads!";
exchange.sendResponseHeaders(200, response.getBytes().length);
exchange.getResponseBody().write(response.getBytes());
}
});
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); // 使用虚拟线程池
server.start();
System.out.println("Server started on port 8080");
}
}
优势:
- 每个请求由独立的虚拟线程处理,支持高并发。
- 简化了异步编程模型,代码风格更接近同步编程。
2. 并发任务处理
使用虚拟线程并发执行多个任务:
import java.util.concurrent.Executors;
public class VirtualThreadTaskExample {
public static void main(String[] args) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
System.out.println("Task executed by: " + Thread.currentThread());
});
}
}
}
}
优势:
- 可以轻松创建数千个并发任务,而不会耗尽系统资源。
总结
虚拟线程的主要优势
- 高并发性能:支持数百万级线程,适合高并发场景。
- 简化编程模型:支持阻塞式编程风格,减少异步编程的复杂性。
- 高效资源利用:阻塞操作不会占用操作系统线程,内存利用率更高。
- 工具兼容性:与普通线程共享相同的调试和监控工具。
适用场景
- 高并发 Web 服务。
- 实时数据处理。
- 并发任务执行。
SpringBoot Undertow 使用虚拟线程
以下是一个 Spring Boot 项目,使用 Undertow 作为 Web 容器,结合 Spring WebFlux 和 JDK 21 虚拟线程 的示例。
项目说明
- Web 容器:使用 Undertow 替代默认的 Tomcat。
- 反应式框架:使用 Spring WebFlux 处理异步请求。
- 虚拟线程:通过虚拟线程处理每个请求,提升高并发性能。
完整示例代码
1. Maven 依赖
在 pom.xml 中添加以下依赖:
<dependencies>
<!-- Spring Boot Starter for WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Undertow Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
2. 配置 Undertow 使用虚拟线程
在 Spring Boot 中,可以通过自定义 Undertow 的线程池来使用虚拟线程。
import io.undertow.UndertowOptions;
import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
@Configuration
public class UndertowConfig {
@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowCustomizer() {
return factory -> factory.addBuilderCustomizers((UndertowBuilderCustomizer) builder -> {
// 使用虚拟线程作为 Undertow 的线程池
builder.setWorkerOption(UndertowOptions.WORKER_TASK_CORE_THREADS, 0);
builder.setWorkerOption(UndertowOptions.WORKER_TASK_MAX_THREADS, Integer.MAX_VALUE);
builder.setWorkerExecutor(Executors.newVirtualThreadPerTaskExecutor());
});
}
}
3. 创建 WebFlux 控制器
使用 Spring WebFlux 的 Mono 和 Flux 处理异步请求。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class VirtualThreadController {
@GetMapping("/virtual-thread")
public Mono<String> handleRequest() {
return Mono.fromCallable(() -> {
System.out.println("Handling request in thread: " + Thread.currentThread());
Thread.sleep(1000); // 模拟阻塞操作
return "Handled by virtual thread!";
});
}
}
4. 主应用程序
启动 Spring Boot 应用程序。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class VirtualThreadApplication {
public static void main(String[] args) {
SpringApplication.run(VirtualThreadApplication.class, args);
}
}
运行说明
-
启动应用程序后,访问
http://localhost:8080/virtual-thread。 -
控制台会打印当前处理请求的线程信息,例如:
Handling request in thread: VirtualThread[#1]/runnable@ForkJoinPool-1-worker-1 -
返回的响应为:
Handled by virtual thread!
关键点说明
-
Undertow 的线程池:
- 默认情况下,Undertow 使用传统的线程池来处理请求。
- 在配置中,我们将 Undertow 的线程池替换为基于虚拟线程的线程池(
Executors.newVirtualThreadPerTaskExecutor())。
-
Spring WebFlux 的异步处理:
- WebFlux 使用
Mono和Flux处理异步请求。 - 在示例中,
Mono.fromCallable将阻塞操作封装为异步任务,并交由虚拟线程执行。
- WebFlux 使用
-
虚拟线程的优势:
- 每个请求由独立的虚拟线程处理,支持数百万级并发。
- 阻塞操作(如
Thread.sleep)不会占用操作系统线程,资源利用率更高。
虚拟线程的好处
-
高并发性能:
- 虚拟线程支持数百万级并发,适合高并发 Web 服务。
-
简化编程模型:
- 支持阻塞式编程风格,开发者无需复杂的异步回调。
-
资源利用率更高:
- 阻塞操作不会占用操作系统线程,释放更多资源。
-
与 WebFlux 无缝集成:
- 虚拟线程可以直接替换传统线程池,兼容 WebFlux 的异步编程模型。