Spring Boot 4.x 虚拟线程深度实践:告别阻塞式编程 虚拟线程概述 Java 19 引入的虚拟线程(Virtual Threads)是 JDK 历史上最重要的并发改进之一。Spring Boot 4.x 深度集成了这一特性,使得开发者可以用熟悉的阻塞式编程模型,获得异步非阻塞的性能优势。 为什么需要虚拟线程 传统线程模型的问题: 虚拟线程的优势: 轻量级:每个虚拟线程仅占用几百字节 可扩展:单 JVM 可创建数百万个虚拟线程 无需学习 Reactive 编程模型 代码可读性高,易于调试 Spring Boot 4.
Java 19 引入的虚拟线程(Virtual Threads)是 JDK 历史上最重要的并发改进之一。Spring Boot 4.x 深度集成了这一特性,使得开发者可以用熟悉的阻塞式编程模型,获得异步非阻塞的性能优势。
传统线程模型的问题:
// 传统方式:每个请求一个线程 @RestController public class UserController { // 1000 个并发请求 = 1000 个平台线程 // 每个平台线程占用 ~1MB 栈内存 // 上下文切换成本高 @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { // 数据库查询 - 阻塞当前线程 User user = userRepository.findById(id); // 调用外部 API - 阻塞当前线程 Profile profile = profileService.getProfile(user.getProfileId()); return user; } }
虚拟线程的优势:
# application.yml spring: threads: virtual: enabled: true # Spring Boot 4.x 默认启用 mvc: async: request-timeout: 30000 # 异步请求超时
@Configuration public class TomcatVirtualThreadConfig { @Bean public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() { return factory -> { factory.addConnectorCustomizers(connector -> { ProtocolHandler protocol = connector.getProtocolHandler(); if (protocol instanceof AbstractProtocol) { // 使用虚拟线程执行器 Executor executor = Executors.newVirtualThreadPerTaskExecutor(); ((AbstractProtocol<?>) protocol).setExecutor(executor); } }); }; } }
@SpringBootTest class VirtualThreadIntegrationTest { @Test void verifyVirtualThreadsEnabled() { // Spring Boot 4.x 应该自动启用虚拟线程 assertThat(Thread.currentThread()) .isInstanceOf(VirtualThread.class); } }
@RestController @RequestMapping("/api/orders") @RequiredArgsConstructor public class OrderController { private final OrderRepository orderRepository; private final InventoryService inventoryService; private final PaymentService paymentService; private final NotificationService notificationService; // 传统方式:每个请求占用一个平台线程 // 虚拟线程:可以轻松处理 10 万并发请求 @PostMapping("/{id}/checkout") public Order checkout(@PathVariable Long id) { // 所有调用都是阻塞的,但在虚拟线程中高效执行 Order order = orderRepository.findById(id) .orElseThrow(() -> new OrderNotFoundException(id)); // 库存检查 - 虚拟线程在此挂起,不占用平台线程 inventoryService.reserve(order.getItems()); // 支付处理 - 可能需要几秒钟 paymentService.process(order.getPayment()); // 发送通知 - 异步但代码看起来是同步的 notificationService.sendConfirmation(order); return order; } }
@Service @RequiredArgsConstructor public class DataImportService { private final RestTemplate restTemplate; private final DataRepository dataRepository; // 使用虚拟线程并行处理大量数据 public void importData(List<String> urls) { try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { List<Future<Void>> futures = urls.stream() .map(url -> executor.submit(() -> { // 每个 URL 在独立的虚拟线程中处理 String data = restTemplate.getForObject(url, String.class); dataRepository.save(parseData(data)); return null; })) .toList(); // 等待所有任务完成 for (Future<Void> future : futures) { future.get(); // 阻塞当前虚拟线程,不影响平台线程 } } catch (InterruptedException | ExecutionException e) { Thread.currentThread().interrupt(); throw new ImportException("数据导入失败", e); } } }
@Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:postgresql://localhost/mydb"); config.setUsername("user"); config.setPassword("password"); // 虚拟线程时代:连接池可以更小 // 传统:200 个连接对应 200 个请求 // 虚拟线程:20 个连接可以服务 10000 个请求 config.setMaximumPoolSize(20); // 显著减少 config.setMinimumIdle(5); // 连接超时可以设置得更短 config.setConnectionTimeout(2000); // 2 秒 config.setIdleTimeout(30000); // 30 秒 return new HikariDataSource(config); } }
// ❌ 错误:synchronized 会 pin 虚拟线程到平台线程 @Service public class CacheService { private final Map<String, Object> cache = new HashMap<>(); public synchronized Object get(String key) { // 避免这样做 return cache.get(key); } } // ✅ 正确:使用 ReentrantLock @Service public class CacheService { private final Map<String, Object> cache = new HashMap<>(); private final ReentrantLock lock = new ReentrantLock(); public Object get(String key) { lock.lock(); try { return cache.get(key); } finally { lock.unlock(); } } }
@Service public class LongRunningTask { private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); public CompletableFuture<Result> executeTask(TaskRequest request) { CompletableFuture<Result> future = new CompletableFuture<>(); Future<?> task = executor.submit(() -> { try { // 定期检查中断状态 while (!Thread.currentThread().isInterrupted()) { // 处理任务 Result result = processStep(request); if (result.isComplete()) { future.complete(result); break; } } } catch (InterruptedException e) { future.completeExceptionally(e); Thread.currentThread().interrupt(); } catch (Exception e) { future.completeExceptionally(e); } }); // 支持取消 future.whenComplete((result, ex) -> { if (!future.isDone()) { task.cancel(true); } }); return future; } }
@Component public class VirtualThreadMonitor { private final MeterRegistry meterRegistry; @Scheduled(fixedRate = 5000) public void monitorVirtualThreads() { // 获取当前 JVM 的虚拟线程数量 ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); long threadCount = threadBean.getThreadCount(); long peakThreadCount = threadBean.getPeakThreadCount(); // 记录指标 Gauge.builder("jvm.virtual.threads.count", threadCount) .register(meterRegistry); Gauge.builder("jvm.virtual.threads.peak", peakThreadCount) .register(meterRegistry); // 获取线程 dump(包含虚拟线程) ThreadInfo[] threadInfos = threadBean.dumpAllThreads(false, false); long virtualThreadCount = Arrays.stream(threadInfos) .filter(info -> info.getThreadName().startsWith("VirtualThread")) .count(); log.debug("虚拟线程数量: {}", virtualThreadCount); } }
@Component @RequiredArgsConstructor public class VirtualThreadGracefulShutdown implements SmartLifecycle { private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); private volatile boolean running = true; @Override public void start() { running = true; } @Override public void stop() { running = false; executor.shutdown(); // 停止接受新任务 try { // 等待现有任务完成 if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { executor.shutdownNow(); // 强制关闭 } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); } } @Override public boolean isRunning() { return running; } }
@SpringBootTest @AutoConfigureMockMvc class VirtualThreadPerformanceTest { @Autowired private MockMvc mockMvc; @Test void performanceComparison() throws Exception { int requests = 10000; int threads = 200; // 测试虚拟线程性能 CountDownLatch latch = new CountDownLatch(requests); long start = System.currentTimeMillis(); try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < requests; i++) { executor.submit(() -> { try { mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()); } finally { latch.countDown(); } }); } } latch.await(); long duration = System.currentTimeMillis() - start; System.out.printf("完成 %d 个请求,耗时: %d ms\n", requests, duration); System.out.printf("吞吐量: %.2f 请求/秒\n", (requests * 1000.0) / duration); } }
| 指标 | 平台线程 | 虚拟线程 | 提升 |
|---|---|---|---|
| 并发请求 | 200 | 100,000+ | 500x |
| 内存占用 (1000 线程) | ~1GB | ~50MB | 20x |
| 启动时间 | 1ms | 0.01ms | 100x |
| 上下文切换 | 高 | 极低 | 显著 |
// ❌ 错误:在 synchronized 块中执行 I/O public void processData() { synchronized (lock) { // 这会导致虚拟线程被固定到平台线程 String data = restTemplate.getForObject(url, String.class); } } // ✅ 正确:使用 ReentrantLock private final ReentrantLock lock = new ReentrantLock(); public void processData() { lock.lock(); try { // 可以安全地在虚拟线程中使用 String data = restTemplate.getForObject(url, String.class); } finally { lock.unlock(); } }
// ❌ 错误:为每个小任务创建虚拟线程 for (int i = 0; i < 1000000; i++) { Thread.startVirtualThread(() -> { tinyOperation(); // 太轻量了,不值得 }); } // ✅ 正确:批量处理 List<Runnable> tasks = ...; try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { tasks.forEach(executor::submit); }
// ❌ 错误:无限创建虚拟线程 while (true) { Thread.startVirtualThread(() -> { connection.query("SELECT * FROM large_table"); // 数据库连接耗尽 }); } // ✅ 正确:使用信号量限制并发 Semaphore semaphore = new Semaphore(50); // 最多 50 个并发查询 while (true) { Thread.startVirtualThread(() -> { try { semaphore.acquire(); connection.query("SELECT * FROM large_table"); } finally { semaphore.release(); } }); }
Spring Boot 4.x 的虚拟线程支持使得 Java 开发者可以用简单的阻塞式代码,实现高性能的并发应用。关键要点:
虚拟线程不是银弹,但对于大多数 Web 应用和服务端程序,它提供了一个简单而强大的并发解决方案。随着 Spring Boot 4.x 的普及,虚拟线程将成为 Java 开发的默认选择。