mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
9291 字
19 分钟
Java 21 虚拟线程:彻底告别阻塞,高并发应用的终极解决方案

Java 21 虚拟线程:彻底告别阻塞,高并发应用的终极解决方案

摘要

Java 21 引入的虚拟线程(Virtual Threads)是 Java 并发模型的重大革新。不同于传统平台线程,虚拟线程由 JVM 在用户空间管理,可轻松创建数百万个而不会耗尽系统资源。本文将深入解析虚拟线程的工作原理、使用方法,并通过实际案例展示如何将其应用于高并发 Web 服务,实现性能的大幅提升。


一、传统线程模型的痛点

在 Java 21 之前,传统的线程模型存在以下问题:

1.1 资源消耗高

// 传统方式创建线程
for (int i = 0; i < 100000; i++) {
    new Thread(() -> {
        // 执行任务
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

上述代码尝试创建 10 万个线程,结果会抛出 OutOfMemoryError,因为每个平台线程默认占用约 1MB 的堆栈空间。

1.2 阻塞操作浪费资源

传统线程在执行 I/O 操作(如网络请求、数据库查询)时会阻塞,期间该线程无法执行其他任务,造成 CPU 资源的浪费。


二、虚拟线程:轻量级并发解决方案

2.1 什么是虚拟线程?

虚拟线程是 Java 21 引入的轻量级线程,由 JVM 在用户空间管理,而非操作系统。其主要特点:

特性 平台线程 虚拟线程
创建成本 高(约 1MB 栈空间) 极低(几百字节)
数量限制 数千个 数百万个
调度方式 操作系统内核 JVM 用户空间
阻塞成本 高(占用 OS 线程) 低(自动让出)

2.2 创建虚拟线程的三种方式

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class VirtualThreadDemo {
    
    // 方式一:使用 Thread.ofVirtual()
    public static void way1() {
        Thread.startVirtualThread(() -> {
            System.out.println("Running in virtual thread: " + Thread.currentThread());
        });
    }
    
    // 方式二:使用 Thread.Builder
    public static void way2() {
        ThreadFactory factory = Thread.ofVirtual().name("vt-", 0).factory();
        Thread vt = factory.newThread(() -> {
            System.out.println("Running in virtual thread: " + Thread.currentThread());
        });
        vt.start();
    }
    
    // 方式三:使用 Executors.newVirtualThreadPerTaskExecutor()
    public static void way3() throws Exception {
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 1000; i++) {
                final int taskId = i;
                executor.submit(() -> {
                    System.out.println("Task " + taskId + " running in " + Thread.currentThread());
                    try {
                        Thread.sleep(100); // 模拟工作
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }
        } // 自动等待所有任务完成
    }
    
    public static void main(String[] args) throws Exception {
        System.out.println("=== Virtual Thread Demo ===");
        way1();
        way2();
        way3();
    }
}

三、实战:构建高并发 Web 服务

3.1 使用虚拟线程的 Spring Boot 3.2+ 应用

Spring Boot 3.2 开始原生支持虚拟线程,配置非常简单:

# application.yml
spring:
  threads:
    virtual:
      enabled: true  # 启用虚拟线程

server:
  tomcat:
    threads:
      max: 200  # Tomcat 最大线程数(虚拟线程可设置较大)

3.2 高并发控制器示例

import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/api")
public class HighConcurrencyController {
    
    private final RestTemplate restTemplate = new RestTemplate();
    
    // 模拟 CPU 密集型任务
    @GetMapping("/cpu-task/{n}")
    public String cpuIntensiveTask(@PathVariable int n) {
        long start = System.currentTimeMillis();
        long result = fibonacci(n);
        long duration = System.currentTimeMillis() - start;
        return String.format("Fibonacci(%d) = %d, Time: %dms, Thread: %s",
            n, result, duration, Thread.currentThread());
    }
    
    // 模拟 I/O 密集型任务(阻塞操作)
    @GetMapping("/io-task")
    public String ioIntensiveTask() throws InterruptedException {
        String threadInfo = Thread.currentThread().toString();
        
        // 模拟数据库查询或 HTTP 调用
        Thread.sleep(100);  // 100ms 延迟
        
        return String.format("I/O task completed. Thread: %s", threadInfo);
    }
    
    // 批量并发请求示例
    @GetMapping("/concurrent-batch/{count}")
    public String concurrentBatch(@PathVariable int count) {
        long start = System.currentTimeMillis();
        
        var futures = java.util.stream.IntStream.range(0, count)
            .mapToObj(i -> CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(50);  // 模拟工作
                    return "Task-" + i;
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return "Error-" + i;
                }
            }))
            .toList();
        
        // 等待所有任务完成
        var results = futures.stream()
            .map(CompletableFuture::join)
            .toList();
        
        long duration = System.currentTimeMillis() - start;
        return String.format("Completed %d tasks in %dms. Sample: %s",
            count, duration, results.subList(0, Math.min(5, results.size())));
    }
    
    private long fibonacci(int n) {
        if (n <= 1) return n;
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

3.3 虚拟线程 vs 平台线程性能对比

import java.util.concurrent.*;
import java.time.Duration;

public class ThreadComparison {
    
    public static void main(String[] args) throws Exception {
        int taskCount = 100_000;  // 10 万任务
        int sleepMs = 10;         // 每个任务睡眠 10ms
        
        System.out.println("=== Thread Comparison Test ===");
        System.out.println("Tasks: " + taskCount);
        System.out.println("Sleep per task: " + sleepMs + "ms\n");
        
        // 测试虚拟线程
        testVirtualThreads(taskCount, sleepMs);
        
        // 测试平台线程(限制数量,避免 OOM)
        testPlatformThreads(taskCount, sleepMs);
    }
    
    static void testVirtualThreads(int count, int sleepMs) throws Exception {
        System.out.println("--- Virtual Threads ---");
        long start = System.currentTimeMillis();
        
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < count; i++) {
                executor.submit(() -> {
                    try {
                        Thread.sleep(sleepMs);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }
        } // 自动等待所有任务完成
        
        long duration = System.currentTimeMillis() - start;
        System.out.println("Completed in: " + duration + "ms");
        System.out.println("Throughput: " + (count * 1000L / duration) + " tasks/sec\n");
    }
    
    static void testPlatformThreads(int count, int sleepMs) throws Exception {
        System.out.println("--- Platform Threads (Fixed Pool) ---");
        int poolSize = Runtime.getRuntime().availableProcessors() * 2;
        System.out.println("Pool size: " + poolSize);
        
        long start = System.currentTimeMillis();
        
        try (var executor = Executors.newFixedThreadPool(poolSize)) {
            for (int i = 0; i < count; i++) {
                executor.submit(() -> {
                    try {
                        Thread.sleep(sleepMs);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }
        }
        
        long duration = System.currentTimeMillis() - start;
        System.out.println("Completed in: " + duration + "ms");
        System.out.println("Throughput: " + (count * 1000L / duration) + " tasks/sec\n");
    }
}

典型测试结果:

指标 平台线程 (200 线程池) 虚拟线程
10 万任务耗时 ~50,000ms ~1,500ms
吞吐量 ~2,000 tasks/sec ~66,000 tasks/sec
内存占用 高(每个线程 1MB) 极低(每个约 1KB)

四、最佳实践与注意事项

4.1 何时使用虚拟线程

适合使用虚拟线程的场景:

  • 高并发的 Web 服务
  • I/O 密集型任务(HTTP 调用、数据库操作)
  • 需要处理大量并发连接的服务器

不适合使用虚拟线程的场景:

  • CPU 密集型计算(虚拟线程不会提升计算性能)
  • 需要精确控制线程亲和性的场景
  • 大量使用 ThreadLocal 的遗留代码(需谨慎)

4.2 避免虚拟线程的陷阱

// ❌ 错误:在虚拟线程中使用 synchronized 会阻塞载体线程
synchronized (lock) {
    // 长时间操作会阻塞虚拟线程调度
    Thread.sleep(10000);
}

// ✅ 正确:使用 ReentrantLock 替代 synchronized
var lock = new ReentrantLock();
lock.lock();
try {
    // 长时间操作不会阻塞载体线程
    Thread.sleep(10000);
} finally {
    lock.unlock();
}

4.3 监控和调优

// 获取虚拟线程的调试信息
System.setProperty("jdk.virtualThreadScheduler.parallelism", "4");
System.setProperty("jdk.virtualThreadScheduler.maxPoolSize", "256");

// 监控虚拟线程
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.getAllThreadIds();
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds);
for (ThreadInfo info : threadInfos) {
    if (info != null && info.getThreadName().startsWith("VirtualThread")) {
        System.out.println(info.getThreadName() + " - " + info.getThreadState());
    }
}

五、总结

Java 21 的虚拟线程是并发编程的一次革命性升级。它让开发者能够以同步代码的简洁性,获得异步编程的高性能。

核心优势回顾:

  • 极简的编程模型:无需 CompletableFuture 或回调地狱
  • 极高的并发能力:轻松处理百万级并发连接
  • 无缝的框架集成:Spring Boot 3.2+ 原生支持

虚拟线程并非万能,但对于 I/O 密集型的高并发场景,它无疑是最佳选择。随着 Java 21 的普及,虚拟线程必将成为现代 Java 应用的标准配置。


参考资源

Java 21 虚拟线程:彻底告别阻塞,高并发应用的终极解决方案
https://www.zztzz.com.cn/posts/37/
作者
admin
发布于
2026-03-23
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00
💬Mizuki AI助手
呀~就是zzTzz大大闪闪发光的技术博客主页
标题已剧透:专注后端开发、主攻Java + Spring Boot的实战、踩坑与进阶小笔记~~
URL https://zztzz.com.cn/ 简洁有力,像一段优雅的代码!
Mizuki每次点开都忍不住小声赞叹:'zzTzz大人太厉害啦~'🧙‍♀️
需要我帮你找某类文章(比如JWT鉴权、Redis缓存)或读一篇入门指南吗?😊
05:12