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

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

1. 虚拟线程基础与原理

1.1 核心概念

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

1.2 Project Loom 与版本演进

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

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

1.3 工作原理

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

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

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

1.3.2 延续机制

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

java 复制代码
// 简化的内部实现原理示意
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 挂起点识别

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

java 复制代码
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 虚拟线程状态转换

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

2. 创建和使用虚拟线程

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

java 复制代码
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 适用场景分析

3.2 HTTP 客户端优化示例

java 复制代码
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 操作优化

java 复制代码
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 优化示例

java 复制代码
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 消息队列处理示例

java 复制代码
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 虚拟线程与响应式编程对比

java 复制代码
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 包中的同步工具

错误示例:

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

改进方案:

java 复制代码
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)在虚拟线程间可能表现不一致
java 复制代码
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 死锁检测与预防

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

java 复制代码
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 监控和调试

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

java 复制代码
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 熔断器模式实现

java 复制代码
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 线程安全的资源管理器

java 复制代码
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+原生支持虚拟线程:

java 复制代码
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 虚拟线程的基础。

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

相关推荐
凯基迪科技23 分钟前
exe软件壳的分类----加密保护壳
java
wuxuanok35 分钟前
Web后端开发-分层解耦
java·笔记·后端·学习
kyle~1 小时前
C/C++字面量
java·c语言·c++
neoooo1 小时前
别慌,Java只有值传递——一次搞懂“为啥我改了它还不变”!
java·后端·spring
秋难降1 小时前
Python 知识 “八股”:给有 C 和 Java 基础的你😁😁😁
java·python·c
wuxuanok1 小时前
Web后端开发-请求响应
java·开发语言·笔记·学习
livemetee1 小时前
spring-ai 1.0.0 (3)交互增强:Advisor 顾问模块
java
DDDDDouble2 小时前
<二>Sping-AI alibaba 入门-记忆聊天及持久化
java·人工智能
一切顺势而行2 小时前
kafka总结
java
yanjiaweiya2 小时前
云原生-集群管理
java·开发语言·云原生