Java 虚拟线程技术详解:原理、实践与优化(上)

88 阅读13分钟

Java 虚拟线程是 JDK 21 引入的重要并发特性,通过轻量级线程实现,能够以极低的资源成本支持百万级并发。本文深入分析虚拟线程的工作原理、使用方法和性能优化技术。

1. 虚拟线程基础与原理

1.1 核心概念

虚拟线程是 Java 平台线程模型的轻量级实现,由 JVM 管理而非直接由操作系统管理。它们采用 M:N 调度模型,将大量虚拟线程映射到少量操作系统线程上。

核心概念.png

1.2 Project Loom 与版本演进

虚拟线程是 Project Loom 项目的核心部分,该项目旨在彻底改革 Java 并发模型。Loom 不仅包括虚拟线程,还包括结构化并发 API 和作用域变量等特性。

  • JDK 19: 首次引入预览特性 (JEP 425)
  • JDK 20: 第二轮预览,改进 API 和性能 (JEP 436)
  • JDK 21: 正式发布为标准特性 (JEP 444)

1.3 工作原理

虚拟线程采用协作式调度机制,核心是挂起和恢复操作:

工作原理.png

1.3.1 协作式调度 vs 抢占式调度

特性协作式调度(虚拟线程)抢占式调度(平台线程)
线程切换时机线程在挂起点主动让出 CPU操作系统强制中断线程执行
调度控制JVM 负责调度操作系统调度器控制
上下文切换开销轻量级(仅保存 JVM 状态)重量级(保存完整 CPU 状态)
资源效率高(一个 OS 线程支持多个虚拟线程)低(每个线程需要独立 OS 资源)
CPU 密集型任务可能阻塞其他虚拟线程时间片机制防止单线程独占 CPU

1.3.2 延续机制

虚拟线程的底层实现依赖于 JDK 中的 Continuation API,用于捕获和恢复执行状态:

// 简化的内部实现原理示意
class VirtualThread {
    private Continuation continuation;

    VirtualThread(Runnable task) {
        // 创建延续,封装任务执行状态
        this.continuation = new Continuation(
            (scope) -> {
                task.run();
                return null;
            },
            Continuation.Scope.FULL
        );
    }

    void run() {
        // 遇到挂起点时,保存当前状态并返回
        continuation.run();
    }

    boolean isYieldPoint() {
        // 检测当前执行点是否为挂起点
        return continuation.isPreempted();
    }
}

1.3.3 挂起点识别

识别代码中可能导致线程固定的方法:

import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;

// 注意:需要--add-exports java.base/jdk.internal.vm=ALL-UNNAMED运行选项
public class PinningDetector {
    public static boolean isPinningOperation(Runnable operation) {
        final ContinuationScope scope = new ContinuationScope("TEST");
        final boolean[] pinned = {false};

        Continuation cont = new Continuation(scope, () -> {
            operation.run();
        });

        // 启动延续
        cont.run();

        // 如果无法挂起,则是固定操作
        return !cont.isPreempted();
    }
}

1.3.4 虚拟线程状态转换

虚拟线程的生命周期包含以下状态转换:

虚拟线程状态转换.png

2. 创建和使用虚拟线程

JDK 21 提供多种创建方式,推荐使用 ExecutorService 方式管理生命周期:

import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.IntStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;

public class VirtualThreadDemo {
    private static final Logger logger = LoggerFactory.getLogger(VirtualThreadDemo.class);

    public static void main(String[] args) {
        // 方法1:直接启动单个虚拟线程
        Thread vThread = Thread.startVirtualThread(() -> {
            // 添加MDC上下文,提高日志可追踪性
            String threadId = UUID.randomUUID().toString();
            MDC.put("threadId", threadId);
            try {
                logger.info("执行于: {}", Thread.currentThread());
            } finally {
                MDC.remove("threadId");
            }
        });

        // 方法2:使用Builder API
        Thread vThread2 = Thread.ofVirtual()
                .name("custom-vthread")
                .uncaughtExceptionHandler((t, e) ->
                    logger.error("线程 {} 发生异常", t.getName(), e))
                .start(() -> {
                    logger.info("命名虚拟线程执行");
                });

        // 方法3:使用ExecutorService(推荐用于管理大量虚拟线程)
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 提交10,000个任务,每个都在独立的虚拟线程中执行
            IntStream.range(0, 10_000).forEach(i -> {
                Future<Integer> future = executor.submit(() -> {
                    try {
                        // 模拟I/O操作
                        Thread.sleep(Duration.ofMillis(100));
                        return i;
                    } catch (InterruptedException e) {
                        // 正确处理中断
                        Thread.currentThread().interrupt();
                        logger.warn("任务{}被中断", i, e);
                        return -1;
                    }
                });
            });
        } // ExecutorService自动关闭

        logger.info("所有任务已提交");
    }

    // 虚拟线程调试辅助方法
    public static void dumpVirtualThreadInfo() {
        if (logger.isDebugEnabled()) {
            Thread.getAllStackTraces().entrySet().stream()
                .filter(e -> e.getKey().isVirtual())
                .forEach(e -> {
                    Thread t = e.getKey();
                    logger.debug("虚拟线程: {} (ID: {}), 状态: {}",
                        t.getName(), t.threadId(), t.getState());
                    for (StackTraceElement ste : e.getValue()) {
                        logger.debug("    at {}", ste);
                    }
                });
        }
    }
}

3. 性能优化最佳方法

3.1 适用场景分析

适用场景分析.png

3.2 HTTP 客户端优化示例

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class OptimizedHttpClient {
    private static final Logger logger = LoggerFactory.getLogger(OptimizedHttpClient.class);

    public static void main(String[] args) throws Exception {
        // 创建HTTP客户端
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(5))
                .followRedirects(HttpClient.Redirect.NORMAL)
                .build();

        List<String> urls = List.of(
                "https://example.com",
                "https://example.org",
                "https://example.net"
        );

        long startTime = System.currentTimeMillis();

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<String>> futures = new ArrayList<>();

            // 提交所有HTTP请求任务
            for (String url : urls) {
                futures.add(executor.submit(() -> {
                    // 添加MDC上下文
                    String requestId = UUID.randomUUID().toString();
                    MDC.put("requestId", requestId);

                    try {
                        logger.info("开始请求: {}", url);

                        try {
                            HttpRequest request = HttpRequest.newBuilder()
                                    .uri(URI.create(url))
                                    .timeout(Duration.ofSeconds(10))
                                    .header("User-Agent", "Java Virtual Threads Demo")
                                    .header("X-Request-ID", requestId)
                                    .GET()
                                    .build();

                            HttpResponse<String> response = client.send(
                                    request, HttpResponse.BodyHandlers.ofString());

                            logger.info("完成请求: {}, 状态码: {}", url, response.statusCode());
                            return String.format("URL: %s, 状态: %d, 内容长度: %d",
                                    url, response.statusCode(), response.body().length());
                        } catch (IOException e) {
                            logger.error("请求{}时发生I/O错误", url, e);
                            return String.format("URL: %s, 错误: I/O异常", url);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            logger.warn("请求{}时被中断", url, e);
                            return String.format("URL: %s, 错误: 被中断", url);
                        } catch (Exception e) {
                            logger.error("请求{}时发生未预期错误", url, e);
                            return String.format("URL: %s, 错误: %s", url, e.getMessage());
                        }
                    } finally {
                        MDC.remove("requestId");
                    }
                }));
            }

            // 获取所有结果,添加超时处理
            for (Future<String> future : futures) {
                try {
                    logger.info(future.get(15, TimeUnit.SECONDS));
                } catch (TimeoutException e) {
                    logger.warn("获取结果超时", e);
                }
            }
        }

        long duration = System.currentTimeMillis() - startTime;
        logger.info("总执行时间: {}ms", duration);
    }
}

3.3 JDBC 操作优化

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class OptimizedJdbcExample {
    private static final Logger logger = LoggerFactory.getLogger(OptimizedJdbcExample.class);

    // 使用HikariCP等连接池
    private final DataSource dataSource;

    public OptimizedJdbcExample(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public List<String> fetchUsernames(List<Integer> userIds) throws Exception {
        List<String> results = new ArrayList<>();

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<String>> futures = new ArrayList<>();

            // 为每个ID创建单独的查询任务
            for (Integer userId : userIds) {
                futures.add(executor.submit(() -> {
                    String queryId = UUID.randomUUID().toString();
                    MDC.put("queryId", queryId);

                    try {
                        logger.debug("开始查询用户ID: {}", userId);

                        // 使用try-with-resources管理连接
                        try (Connection conn = dataSource.getConnection()) {
                            // 注意:此优化特定于MySQL 8.0+驱动,其他数据库驱动可能需要不同配置
                            if (conn.isWrapperFor(com.mysql.cj.jdbc.JdbcConnection.class)) {
                                conn.unwrap(com.mysql.cj.jdbc.JdbcConnection.class)
                                    .getPropertySet()
                                    .getBooleanProperty("useVirtualThreads")
                                    .setValue(true);
                            }

                            // 设置连接超时
                            conn.setNetworkTimeout(Executors.newVirtualThreadPerTaskExecutor(), 5000);

                            String sql = "SELECT username FROM users WHERE id = ?";
                            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                                // 分开设置参数与执行,提高可读性和调试便捷性
                                stmt.setInt(1, userId);
                                try (ResultSet rs = stmt.executeQuery()) {
                                    if (rs.next()) {
                                        return rs.getString("username");
                                    }
                                    return null;
                                }
                            }
                        } catch (SQLException e) {
                            logger.error("查询用户ID={}时发生SQL错误", userId, e);
                            return null;
                        }
                    } finally {
                        MDC.remove("queryId");
                    }
                }));
            }

            // 收集结果,添加超时处理
            for (Future<String> future : futures) {
                try {
                    String username = future.get(10, TimeUnit.SECONDS);
                    if (username != null) {
                        results.add(username);
                    }
                } catch (Exception e) {
                    logger.warn("获取用户名结果失败", e);
                }
            }
        }

        return results;
    }
}

3.4 文件 I/O 优化示例

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OptimizedFileIO {
    private static final Logger logger = LoggerFactory.getLogger(OptimizedFileIO.class);

    public static List<String> processFiles(List<Path> filePaths) {
        List<String> results = new ArrayList<>();

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<String>> futures = new ArrayList<>();

            for (Path path : filePaths) {
                futures.add(executor.submit(() -> {
                    String fileId = UUID.randomUUID().toString();
                    MDC.put("fileId", fileId);

                    try {
                        logger.debug("开始处理文件: {}", path.getFileName());

                        // 使用异步文件通道避免阻塞
                        try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(
                                path, StandardOpenOption.READ)) {

                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            StringBuilder content = new StringBuilder();

                            // 读取文件内容
                            int position = 0;
                            Future<Integer> readFuture;
                            do {
                                buffer.clear();
                                readFuture = channel.read(buffer, position);
                                int bytesRead = readFuture.get();

                                if (bytesRead <= 0) break;

                                buffer.flip();
                                byte[] bytes = new byte[buffer.remaining()];
                                buffer.get(bytes);
                                content.append(new String(bytes));

                                position += bytesRead;
                            } while (true);

                            logger.debug("完成文件处理: {}, 读取字节数: {}", path.getFileName(), position);
                            return path.getFileName() + ": " + content.substring(0,
                                    Math.min(50, content.length())) + "...";
                        }
                    } catch (IOException e) {
                        logger.error("读取文件{}时出错", path, e);
                        return path.getFileName() + ": 读取错误";
                    } catch (Exception e) {
                        logger.error("处理文件{}时出错", path, e);
                        return path.getFileName() + ": 处理错误";
                    } finally {
                        MDC.remove("fileId");
                    }
                }));
            }

            for (Future<String> future : futures) {
                try {
                    results.add(future.get());
                } catch (Exception e) {
                    logger.error("获取文件处理结果时出错", e);
                }
            }
        }

        return results;
    }
}

3.5 消息队列处理示例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MessageQueueProcessor {
    private static final Logger logger = LoggerFactory.getLogger(MessageQueueProcessor.class);

    // 使用信号量实现背压机制
    private final Semaphore concurrencyLimiter;
    private final AtomicInteger activeThreads = new AtomicInteger(0);

    public MessageQueueProcessor(int maxConcurrency) {
        this.concurrencyLimiter = new Semaphore(maxConcurrency);
    }

    public void startProcessing(MessageQueue queue) {
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            while (!Thread.currentThread().isInterrupted()) {
                // 实现背压控制
                if (concurrencyLimiter.tryAcquire()) {
                    try {
                        Message message = queue.poll();
                        if (message == null) {
                            concurrencyLimiter.release();
                            Thread.sleep(100); // 队列为空时短暂暂停
                            continue;
                        }

                        // 提交处理任务
                        executor.submit(() -> {
                            String messageId = message.getId();
                            MDC.put("messageId", messageId);

                            try {
                                int current = activeThreads.incrementAndGet();
                                logger.info("开始处理消息ID={}, 当前活动线程: {}",
                                        messageId, current);

                                processMessageWithRetry(message);
                            } catch (Exception e) {
                                logger.error("处理消息{}时出错", messageId, e);
                                // 根据业务需求处理失败消息
                                queue.retry(message);
                            } finally {
                                activeThreads.decrementAndGet();
                                concurrencyLimiter.release();
                                MDC.remove("messageId");
                            }
                        });
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                } else {
                    // 等待资源释放
                    Thread.sleep(50);
                }
            }
        } catch (Exception e) {
            logger.error("消息处理主循环异常", e);
        }
    }

    // 带指数退避的重试机制
    private void processMessageWithRetry(Message message) throws Exception {
        int attempts = 0;
        int maxAttempts = 3;
        long initialDelayMs = 100;

        while (attempts < maxAttempts) {
            try {
                processMessage(message);
                return; // 成功处理,返回
            } catch (Exception e) {
                attempts++;
                if (attempts >= maxAttempts) {
                    throw e; // 重试次数用尽,抛出异常
                }

                // 计算指数退避延迟时间
                long delayMs = initialDelayMs * (1L << (attempts - 1));
                logger.warn("处理消息{}失败,将在{}ms后进行第{}次重试",
                        message.getId(), delayMs, attempts + 1);
                Thread.sleep(delayMs);
            }
        }
    }

    private void processMessage(Message message) {
        // 消息处理逻辑
    }

    // 接口定义
    interface MessageQueue {
        Message poll();
        void retry(Message message);
    }

    interface Message {
        String getId();
        byte[] getPayload();
    }
}

3.6 虚拟线程与响应式编程对比

import reactor.core.publisher.Flux;
import java.util.List;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReactiveVsVirtualThreads {
    private static final Logger logger = LoggerFactory.getLogger(ReactiveVsVirtualThreads.class);

    public static void main(String[] args) {
        List<String> urls = List.of(
            "https://example.com",
            "https://example.org",
            "https://example.net"
        );

        // 响应式编程方式
        logger.info("使用响应式编程处理...");
        Flux.fromIterable(urls)
            .flatMap(url -> makeReactiveHttpRequest(url))
            .doOnNext(response -> logger.info("响应式响应: {}", response))
            .subscribe();

        // 虚拟线程方式
        logger.info("使用虚拟线程处理...");
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (String url : urls) {
                executor.submit(() -> {
                    String response = makeBlockingHttpRequest(url);
                    logger.info("虚拟线程响应: {}", response);
                    return response;
                });
            }
        }
    }

    // 模拟方法
    private static Flux<String> makeReactiveHttpRequest(String url) {
        return Flux.just("Response from " + url);
    }

    private static String makeBlockingHttpRequest(String url) {
        return "Response from " + url;
    }
}

4. 常见问题与解决方案

4.1 线程固定问题

当虚拟线程在执行某些操作时无法被挂起,会导致承载线程被固定,降低性能。

线程固定原因和解决方案

原因问题描述解决方案
同步块synchronized 块阻止虚拟线程挂起使用 java.util.concurrent 中的锁代替
原生方法JNI 代码执行期间无法挂起减少原生方法调用或使用异步包装
旧版 JDBC同步阻塞驱动会固定线程使用支持虚拟线程的 JDBC 驱动或 R2DBC
遗留代码使用 Thread.currentThread()的旧代码重构为使用上下文或参数传递
监视器操作Object.wait()等方法使用 concurrent 包中的同步工具

错误示例:

// 不推荐:在虚拟线程中使用同步锁
void processData(List<String> data) {
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        for (String item : data) {
            executor.submit(() -> {
                synchronized (databaseLock) {  // 会导致承载线程被固定
                    performDatabaseOperation(item);
                    return true;
                }
            });
        }
    }
}

改进方案:

import java.util.concurrent.locks.ReentrantLock;

// 推荐:使用java.util.concurrent锁
private final ReentrantLock databaseLock = new ReentrantLock();

void processData(List<String> data) {
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        for (String item : data) {
            executor.submit(() -> {
                // ReentrantLock允许虚拟线程在等待锁时挂起
                databaseLock.lock();
                try {
                    performDatabaseOperation(item);
                    return true;
                } finally {
                    databaseLock.unlock();
                }
            });
        }
    }
}

4.2 ThreadLocal 使用问题

虚拟线程拥有自己的 ThreadLocal 变量,需要注意以下几点:

  1. 每个虚拟线程有独立的 ThreadLocal 存储空间
  2. 虚拟线程数量大时,ThreadLocal 可能导致内存问题
  3. 继承式 ThreadLocal(InheritableThreadLocal)在虚拟线程间可能表现不一致
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;

public class ThreadLocalExample {
    private static final Logger logger = LoggerFactory.getLogger(ThreadLocalExample.class);
    private static final ThreadLocal<String> userContext = new ThreadLocal<>();

    public static void main(String[] args) throws Exception {
        userContext.set("MainUser");
        logger.info("主线程用户: {}", userContext.get());

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            executor.submit(() -> {
                // 虚拟线程有自己的ThreadLocal空间
                logger.info("虚拟线程初始用户: {}", userContext.get());  // 输出null

                userContext.set("VirtualUser");
                logger.info("虚拟线程设置后用户: {}", userContext.get());  // 输出VirtualUser
                return null;
            }).get();
        }

        logger.info("主线程用户(不变): {}", userContext.get());  // 仍然是MainUser

        // 重要:大量虚拟线程时,应确保ThreadLocal正确清理
        userContext.remove();
    }

    // 检测ThreadLocal内存泄漏
    public static void checkForThreadLocalLeaks() {
        System.gc();
        // 获取JVM内存使用情况
        Runtime runtime = Runtime.getRuntime();
        long usedMemory = runtime.totalMemory() - runtime.freeMemory();
        logger.info("当前内存使用: {} MB", usedMemory / (1024 * 1024));

        // 如果使用Java Mission Control或JFR,可以监控ThreadLocal实例数量
    }
}

4.3 死锁检测与预防

虚拟线程的死锁情况可能更难检测,因为传统工具可能无法正确显示其状态:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DeadlockDetector {
    private static final Logger logger = LoggerFactory.getLogger(DeadlockDetector.class);

    // 定期检测死锁
    public static void startDeadlockDetection(long intervalMs) {
        Thread detector = Thread.ofVirtual().name("deadlock-detector").start(() -> {
            ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

            while (!Thread.currentThread().isInterrupted()) {
                try {
                    long[] deadlockedThreadIds = threadBean.findDeadlockedThreads();
                    if (deadlockedThreadIds != null && deadlockedThreadIds.length > 0) {
                        logger.error("检测到死锁! 涉及{}个线程", deadlockedThreadIds.length);

                        ThreadInfo[] threadInfos = threadBean.getThreadInfo(deadlockedThreadIds, true, true);
                        for (ThreadInfo info : threadInfos) {
                            logger.error("死锁线程: {}\n锁持有情况: {}\n锁等待情况: {}",
                                info.getThreadName(),
                                info.getLockName(),
                                info.getLockInfo());
                        }
                    }

                    Thread.sleep(intervalMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
    }

    // 死锁预防示例:总是按相同顺序获取锁
    public static class SafeLocking {
        private final ReentrantLock lockA = new ReentrantLock();
        private final ReentrantLock lockB = new ReentrantLock();

        // 安全方法:总是按固定顺序获取锁
        public void safeMethod() {
            lockA.lock();
            try {
                lockB.lock();
                try {
                    // 操作共享资源
                } finally {
                    lockB.unlock();
                }
            } finally {
                lockA.unlock();
            }
        }
    }
}

4.4 监控和调试

虚拟线程的特殊性质需要不同的监控和调试方法:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class VirtualThreadMonitoring {
    private static final Logger logger = LoggerFactory.getLogger(VirtualThreadMonitoring.class);

    public static void startMonitoring() {
        // 使用JDK Flight Recorder监控虚拟线程
        RecordingStream rs = new RecordingStream();

        // 监控虚拟线程创建
        rs.enable("jdk.VirtualThreadStart");
        rs.enable("jdk.VirtualThreadEnd");

        // 监控线程固定
        rs.enable("jdk.VirtualThreadPinned");

        rs.onEvent("jdk.VirtualThreadStart", event -> {
            logger.info("虚拟线程启动: {}", event.getString("threadName"));
        });

        rs.onEvent("jdk.VirtualThreadPinned", event -> {
            logger.warn("虚拟线程固定: {}, 持续时间: {}ms, 原因: {}",
                    event.getString("threadName"),
                    event.getDuration().toMillis(),
                    event.getString("reason", "未知"));
        });

        rs.start();
    }

    public static void analyzeThreadDump() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

        // 获取包含虚拟线程的线程转储
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);

        // 分析虚拟线程
        long virtualThreadCount = Arrays.stream(threadInfos)
                .filter(info -> info.getThreadName().startsWith("VirtualThread"))
                .count();

        // 统计线程状态
        long runningCount = Arrays.stream(threadInfos)
                .filter(info -> info.getThreadState() == Thread.State.RUNNABLE)
                .count();

        long waitingCount = Arrays.stream(threadInfos)
                .filter(info -> info.getThreadState() == Thread.State.WAITING ||
                               info.getThreadState() == Thread.State.TIMED_WAITING)
                .count();

        long blockedCount = Arrays.stream(threadInfos)
                .filter(info -> info.getThreadState() == Thread.State.BLOCKED)
                .count();

        logger.info("线程统计: 总计={}, 虚拟线程={}, 运行中={}, 等待中={}, 阻塞={}",
                threadInfos.length, virtualThreadCount, runningCount, waitingCount, blockedCount);
    }

    // 检测虚拟线程栈溢出风险
    public static void checkForStackOverflowRisks() {
        if (logger.isDebugEnabled()) {
            ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
            ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);

            Arrays.stream(threadInfos)
                .filter(info -> info.getThreadName().startsWith("VirtualThread"))
                .forEach(info -> {
                    StackTraceElement[] stack = info.getStackTrace();
                    if (stack.length > 100) { // 设置栈深度阈值
                        logger.debug("虚拟线程{}栈深度较大({}帧),可能存在栈溢出风险",
                            info.getThreadName(), stack.length);
                    }
                });
        }
    }
}

4.5 熔断器模式实现

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CircuitBreaker {
    private static final Logger logger = LoggerFactory.getLogger(CircuitBreaker.class);

    // 使用枚举定义熔断器状态
    public enum CircuitState {
        CLOSED,    // 正常状态,允许请求通过
        OPEN,      // 熔断状态,拒绝所有请求
        HALF_OPEN  // 半开状态,允许少量请求尝试恢复
    }

    private final AtomicInteger failureCount = new AtomicInteger(0);
    private final AtomicReference<CircuitState> state = new AtomicReference<>(CircuitState.CLOSED);
    private final int threshold;
    private final long resetTimeoutMs;
    private volatile long lastFailureTime;
    private final String name;

    public CircuitBreaker(String name, int threshold, long resetTimeoutMs) {
        this.name = name;
        this.threshold = threshold;
        this.resetTimeoutMs = resetTimeoutMs;
    }

    public <T> T execute(Supplier<T> operation) throws CircuitBreakerOpenException, Exception {
        CircuitState currentState = state.get();

        if (currentState == CircuitState.OPEN) {
            // 检查是否可以尝试半开状态
            if (System.currentTimeMillis() - lastFailureTime > resetTimeoutMs) {
                logger.info("熔断器[{}]尝试半开状态", name);
                // 尝试转换到半开状态
                if (state.compareAndSet(CircuitState.OPEN, CircuitState.HALF_OPEN)) {
                    failureCount.set(0);
                }
            } else {
                logger.warn("熔断器[{}]断开中,拒绝请求", name);
                throw new CircuitBreakerOpenException("Circuit breaker is open");
            }
        }

        try {
            T result = operation.get();
            // 成功执行,重置失败计数并确保关闭状态
            successfulCall();
            return result;
        } catch (Exception e) {
            // 记录失败
            recordFailure();
            throw e;
        }
    }

    private void successfulCall() {
        failureCount.set(0);
        CircuitState currentState = state.get();

        if (currentState != CircuitState.CLOSED) {
            if (state.compareAndSet(currentState, CircuitState.CLOSED)) {
                logger.info("熔断器[{}]关闭,服务恢复正常", name);
            }
        }
    }

    private void recordFailure() {
        lastFailureTime = System.currentTimeMillis();
        int currentFailures = failureCount.incrementAndGet();
        logger.warn("熔断器[{}]记录失败,当前失败次数: {}/{}", name, currentFailures, threshold);

        if (currentFailures >= threshold) {
            CircuitState currentState = state.get();
            if (currentState != CircuitState.OPEN &&
                state.compareAndSet(currentState, CircuitState.OPEN)) {
                logger.error("熔断器[{}]打开,暂停服务{}ms", name, resetTimeoutMs);
            }
        }
    }

    // 熔断器状态
    public CircuitState getState() {
        return state.get();
    }

    public int getFailureCount() {
        return failureCount.get();
    }

    // 专用异常类型
    public static class CircuitBreakerOpenException extends Exception {
        public CircuitBreakerOpenException(String message) {
            super(message);
        }
    }
}

4.6 线程安全的资源管理器

import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程安全的资源管理器,确保资源只被关闭一次
 */
public final class ResourceGuard<T extends AutoCloseable> implements AutoCloseable {
    private static final Logger logger = LoggerFactory.getLogger(ResourceGuard.class);

    private final T resource;
    private final AtomicBoolean closed = new AtomicBoolean(false);
    private final String resourceName;

    public ResourceGuard(T resource) {
        this(resource, resource.getClass().getSimpleName());
    }

    public ResourceGuard(T resource, String resourceName) {
        this.resource = resource;
        this.resourceName = resourceName;
    }

    /**
     * 获取被管理的资源
     * @throws IllegalStateException 如果资源已关闭
     */
    public T get() {
        if (closed.get()) {
            throw new IllegalStateException("Resource " + resourceName + " already closed");
        }
        return resource;
    }

    /**
     * 安全关闭资源,确保只关闭一次
     */
    @Override
    public void close() throws Exception {
        if (closed.compareAndSet(false, true)) {
            try {
                resource.close();
                logger.debug("资源[{}]已安全关闭", resourceName);
            } catch (Exception e) {
                logger.error("关闭资源[{}]时发生错误", resourceName, e);
                throw e;
            }
        } else {
            logger.debug("资源[{}]已经关闭,忽略重复关闭请求", resourceName);
        }
    }

    /**
     * 检查资源是否已关闭
     */
    public boolean isClosed() {
        return closed.get();
    }
}

5. 与框架整合

5.1 Spring Boot 整合

Spring Boot 3.2+原生支持虚拟线程:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.CommonsRequestLoggingFilter;

import java.util.concurrent.Executors;
import jakarta.servlet.Filter;

@SpringBootApplication
@EnableAsync
public class VirtualThreadSpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(VirtualThreadSpringApplication.class, args);
    }

    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
        // 配置Tomcat使用虚拟线程处理请求
        return protocolHandler -> {
            protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        };
    }

    // 配置Spring @Async使用虚拟线程
    @Bean
    public java.util.concurrent.Executor taskExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor();
    }

    // 添加请求日志过滤器
    @Bean
    public Filter requestLoggingFilter() {
        CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
        filter.setIncludeClientInfo(true);
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setMaxPayloadLength(10000);
        filter.setIncludeHeaders(false);
        return filter;
    }

    // 全局异常处理
    @Bean
    public GlobalExceptionHandler globalExceptionHandler() {
        return new GlobalExceptionHandler();
    }

    // 或者使用application.properties配置:
    // spring.threads.virtual.enabled=true

    @RestController
    public static class VirtualThreadController {
        @GetMapping("/blocking")
        public String blockingEndpoint() throws InterruptedException {
            // 直接在虚拟线程中执行阻塞操作
            Thread.sleep(1000);
            return "处理完成,线程: " + Thread.currentThread();
        }

        @Async
        public void asyncMethod() {
            // 此方法将在虚拟线程中执行
        }
    }
}

到目前为止,我们已经深入探讨了虚拟线程的基础原理、创建方法、性能优化技巧、常见问题解决方案以及与主流框架的整合。这些内容为您提供了使用 Java 虚拟线程的基础。

在下一部分中,我们将继续深入探讨虚拟线程的高级应用场景,包括从传统线程池迁移的策略、大规模生产环境部署建议、内部实现原理分析、性能测试对比以及与其他并发模型的对比。还将提供更多实战案例,帮助您在实际项目中充分发挥虚拟线程的潜力。