java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java 21 虚拟线程

Java 21 虚拟线程最佳实践

作者:yb779

本文系统梳理 Java 21 虚拟线程的核心原理、适用场景与落地最佳实践,结合创建示例、结构化并发建议及性能对比,帮助你以更低成本提升高并发服务的吞吐与可维护性

Java 21 的虚拟线程(Virtual Threads)让“一个请求一个线程”重新变得可行。相比传统平台线程,虚拟线程更轻量、创建成本更低、阻塞代价更小,非常适合 I/O 密集型、高并发、请求生命周期短的服务。

但虚拟线程并不是“开了就快”,想真正发挥价值,必须理解它的适用边界、调度特性和最佳实践。本文结合代码示例和性能对比,带你从“能用”走到“用对”。

1. 虚拟线程是什么

虚拟线程由 JVM 管理,底层由少量载体线程(Carrier Threads)执行。它的核心优势是:

你可以把它理解为:线程仍然是线程,但“重量”被 JVM 接管了。

2. 什么时候适合用虚拟线程

虚拟线程特别适合以下场景:

不太适合:

3. 虚拟线程创建示例

示例 1:最简单的虚拟线程创建

public class VirtualThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread vt = Thread.ofVirtual().start(() -> {
            System.out.println("Hello from virtual thread: " + Thread.currentThread());
        });

        vt.join();
    }
}

这段代码与普通线程写法几乎一致,只是把 Thread.ofPlatform() 换成了 Thread.ofVirtual()

示例 2:使用虚拟线程执行多个任务

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class VirtualThreadExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10; i++) {
                int taskId = i;
                executor.submit(() -> {
                    System.out.println("Task " + taskId + " running on " + Thread.currentThread());
                    TimeUnit.MILLISECONDS.sleep(200);
                    return null;
                });
            }
        }
    }
}

newVirtualThreadPerTaskExecutor() 是最推荐的入口之一。它保留了熟悉的 ExecutorService 模型,同时让每个任务都运行在虚拟线程上。

4. 最佳实践:优先把“阻塞型业务”迁移到虚拟线程

如果你的服务中存在大量如下代码:

那么虚拟线程能显著简化并发模型。你可以继续使用同步 API,而不必强行改造成复杂的响应式链路。

推荐做法

5. 最佳实践:谨慎使用线程池的旧思维

虚拟线程时代,很多旧习惯需要更新。

不建议

更合理的方式

6. 性能对比示例:平台线程 vs 虚拟线程

下面用一个简单的 I/O 模拟任务来对比二者差异。注意,这不是严格基准测试,但足以说明趋势。

示例 3:性能对比代码

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadCompareDemo {
    private static final int TASKS = 10_000;

    public static void main(String[] args) throws Exception {
        long platformTime = runPlatformThreads();
        long virtualTime = runVirtualThreads();

        System.out.println("Platform threads cost: " + platformTime + " ms");
        System.out.println("Virtual threads cost:  " + virtualTime + " ms");
    }

    static long runPlatformThreads() throws Exception {
        long start = System.currentTimeMillis();
        try (ExecutorService executor = Executors.newFixedThreadPool(200)) {
            List<java.util.concurrent.Future<?>> futures = new ArrayList<>();
            for (int i = 0; i < TASKS; i++) {
                futures.add(executor.submit(() -> {
                    TimeUnit.MILLISECONDS.sleep(10);
                    return null;
                }));
            }
            for (var f : futures) {
                f.get();
            }
        }
        return System.currentTimeMillis() - start;
    }

    static long runVirtualThreads() throws Exception {
        long start = System.currentTimeMillis();
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<java.util.concurrent.Future<?>> futures = new ArrayList<>();
            for (int i = 0; i < TASKS; i++) {
                futures.add(executor.submit(() -> {
                    TimeUnit.MILLISECONDS.sleep(10);
                    return null;
                }));
            }
            for (var f : futures) {
                f.get();
            }
        }
        return System.currentTimeMillis() - start;
    }
}

结果解读

在 I/O 等待占主导的场景下,虚拟线程通常能以更少的资源支撑更高并发。平台线程池会受到线程数量和上下文切换成本影响,而虚拟线程可以更自然地扩展到海量任务。

但要注意:虚拟线程提升的是并发吞吐和资源利用率,不会让 CPU 密集型任务凭空变快。

7. 最佳实践:避免“载体线程阻塞”问题

虚拟线程虽然轻量,但如果你在其中调用了某些会长期占用载体线程的操作,收益会下降。

例如:

建议:

8. 最佳实践:结合结构化并发提升可维护性

Java 21 还带来了结构化并发的预览特性,它和虚拟线程是天然搭档。

当一个请求需要并行调用多个下游服务时,结构化并发可以让任务管理更清晰:

这比手写一堆 Future 聚合逻辑更易维护。

9. 落地建议:从一个入口开始改造

如果你正在把传统 Java 服务迁移到虚拟线程,建议按以下步骤推进:

  1. 先挑选 I/O 密集型接口
  2. 使用 newVirtualThreadPerTaskExecutor() 替换旧线程池
  3. 保持同步写法,不急于重构业务逻辑
  4. 加上超时、限流、熔断
  5. 观察 CPU、内存、延迟和下游压力
  6. 再逐步扩展到更多链路

10. 总结

Java 21 虚拟线程的最大价值,不是“替代所有线程池”,而是让高并发服务重新回到更简单、更自然的同步编程模型。

记住这几个关键词:

如果你想用更低的复杂度获得更高的并发能力,虚拟线程是 Java 21 时代值得优先尝试的技术方案。

到此这篇关于Java 21 虚拟线程最佳实践的文章就介绍到这了,更多相关Java 21 虚拟线程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文