跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 21 虚拟线程(Virtual Threads)使用指南

详细介绍 Java 21 引入的虚拟线程技术。内容包括前提准备、基础用法(直接启动、手动创建、Future 获取结果)、进阶用法(虚拟线程池、定时任务变通方案)以及 Spring Boot 实战集成。重点强调虚拟线程适用于高并发 I/O 场景,需注意避免 CPU 密集型任务、ThreadLocal 内存泄漏及锁竞争问题,并提供相关代码示例与最佳实践建议。

漫步发布于 2026/3/26更新于 2026/7/2356 浏览
Java 21 虚拟线程(Virtual Threads)使用指南

虚拟线程是 Java 21 正式引入的轻量级用户态线程,核心优势是低开销、高并发、支持同步编程模型实现异步性能。下面将从前提准备、基础用法、进阶用法、实战场景、注意事项五个方面详细讲解具体使用方式。

一、使用前提

  1. Java 版本要求:必须使用 Java 21 及以上版本(Java 19/20 为预览特性,需通过 --enable-preview 启用,不推荐生产环境使用)。

编译运行说明:若使用 Java 19/20(预览版),编译和运行时需添加 --enable-preview 参数:

# 编译
javac --enable-preview -source 20 VirtualThreadDemo.java
# 运行
java --enable-preview VirtualThreadDemo

环境验证:通过以下命令验证 Java 版本,确保满足要求:

java -version
# 预期输出(示例):openjdk version "21.0.1" 2023-10-17 LTS

二、基础用法:创建与启动虚拟线程

虚拟线程的创建和启动方式简洁,支持多种写法,核心 API 位于 java.lang.Thread 类中。

1. 方式 1:直接启动(静态方法快捷创建)

使用 Thread.startVirtualThread(Runnable) 直接创建并启动虚拟线程,无需手动调用 start() 方法,是最简单的使用方式。

public class VirtualThreadBasicDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 1. 启动单个虚拟线程
        Thread.startVirtualThread(() -> {
            // 虚拟线程执行的业务逻辑(支持同步阻塞操作)
            System.out.printf("虚拟线程 1 执行中,线程 ID:%s,是否为虚拟线程:%b%n", 
                Thread.currentThread().threadId(), Thread.currentThread().isVirtual());
            try {
                // 模拟 I/O 阻塞(虚拟线程阻塞时会释放载体线程,不占用内核资源)
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println();
        });
        
        
         (   ; i <= ; i++) {
               i;
            Thread.startVirtualThread(() -> {
                System.out.printf(, threadNum, Thread.currentThread().threadId());
                 {
                    Thread.sleep();
                }  (InterruptedException e) {
                      (e);
                }
                System.out.printf(, threadNum);
            });
        }
        
        
        Thread.sleep();
        System.out.println();
    }
}
"虚拟线程 1 执行完成"
// 2. 批量启动多个虚拟线程(支持百万级并发,资源消耗极低)
for
int
i
=
2
10
int
threadNum
=
"虚拟线程%d执行中,线程 ID:%s%n"
try
500
catch
throw
new
RuntimeException
"虚拟线程%d执行完成%n"
// 主线程等待所有虚拟线程执行完成(实际开发中无需手动等待,虚拟线程由 JVM 管理)
2000
"所有虚拟线程执行完毕,主线程退出"
2. 方式 2:手动创建 + 启动(灵活配置线程属性)

通过 Thread.ofVirtual() 创建 Thread.Builder,可配置虚拟线程名称、优先级等属性,再通过 start() 启动,适合需要自定义线程属性的场景。

public class VirtualThreadBasicDemo2 {
    public static void main(String[] args) throws InterruptedException {
        // 1. 创建虚拟线程构建器,配置线程名称
        Thread virtualThread = Thread.ofVirtual()
            .name("business-virtual-thread-1") // 设置线程名称,便于调试
            .unstarted(() -> { // 定义任务逻辑,暂不启动
                System.out.printf("自定义虚拟线程执行中,线程名称:%s,是否为虚拟线程:%b%n", 
                    Thread.currentThread().getName(), Thread.currentThread().isVirtual());
                try {
                    // 模拟数据库查询阻塞
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("自定义虚拟线程执行完成");
            });
        
        // 2. 手动启动虚拟线程
        virtualThread.start();
        
        // 3. 链式创建并启动(简化写法)
        Thread.ofVirtual()
            .name("business-virtual-thread-2")
            .start(() -> {
                System.out.printf("链式创建虚拟线程执行中,线程名称:%s%n", Thread.currentThread().getName());
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("链式创建虚拟线程执行完成");
            });
        
        // 主线程等待
        Thread.sleep(1500);
        System.out.println("主线程退出");
    }
}
3. 方式 3:通过 Future 获取执行结果(有返回值任务)

若虚拟线程执行的任务需要返回结果,可使用 ExecutorService 配合 Callable,通过 Future 获取返回值,这是实际开发中处理有结果任务的常用方式。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class VirtualThreadFutureDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建虚拟线程池(每个任务对应一个虚拟线程)
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 1. 提交有返回值的任务(Callable)
            Future<String> future1 = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.printf("任务 1 执行中,虚拟线程名称:%s%n", Thread.currentThread().getName());
                    Thread.sleep(1000); // 模拟 I/O 阻塞
                    return "任务 1 执行结果:成功";
                }
            });
            
            // 2. 提交 Lambda 表达式形式的 Callable 任务(简化写法)
            Future<Integer> future2 = executor.submit(() -> {
                System.out.printf("任务 2 执行中,虚拟线程名称:%s%n", Thread.currentThread().getName());
                Thread.sleep(800);
                return 100 + 200; // 业务计算逻辑
            });
            
            // 3. 获取任务返回结果(阻塞获取,若需非阻塞可使用 isDone() 判断)
            String result1 = future1.get();
            Integer result2 = future2.get();
            System.out.println("任务 1 返回结果:" + result1);
            System.out.println("任务 2 返回结果:" + result2);
        }
        // try-with-resources 会自动关闭 executor,无需手动 shutdown()
        System.out.println("所有任务执行完毕,主线程退出");
    }
}

三、进阶用法:虚拟线程池的使用

实际开发中,不推荐手动创建大量虚拟线程,而是使用虚拟线程池管理任务,Java 提供了专门的虚拟线程池实现,核心是 Executors.newVirtualThreadPerTaskExecutor()。

1. 核心特性
  • 每个任务对应一个独立的虚拟线程,任务执行完毕后虚拟线程自动销毁,无需复用。
  • 无需配置核心线程数、最大线程数,JVM 自动管理载体线程(默认载体线程数等于 CPU 核心数)。
  • 实现了 AutoCloseable 接口,可通过 try-with-resources 自动关闭,避免资源泄漏。
  • 支持批量提交任务、定时任务(需配合 ScheduledExecutorService,Java 21 暂未提供原生虚拟线程定时线程池,可通过第三方库或自定义实现)。
2. 批量任务处理示例
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class VirtualThreadPoolBatchDemo {
    public static void main(String[] args) {
        // 创建虚拟线程池
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 批量提交 1000 个任务(支持百万级任务,内存占用仅几十 MB)
            List<Runnable> tasks = IntStream.range(0, 1000)
                .mapToObj(i -> (Runnable) () -> {
                    System.out.printf("批量任务%d执行中,虚拟线程 ID:%s%n", i, Thread.currentThread().threadId());
                    try {
                        // 模拟每个任务的 I/O 阻塞(如数据库查询、HTTP 调用)
                        TimeUnit.MILLISECONDS.sleep(200);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException(e);
                    }
                    System.out.printf("批量任务%d执行完成%n", i);
                })
                .toList();
            
            // 提交所有任务
            tasks.forEach(executor::submit);
            
            // 等待所有任务执行完成(可选,executor 关闭时会等待任务执行完毕)
            System.out.println("所有批量任务已提交,等待执行完成...");
        }
        // 自动关闭线程池,等待所有任务执行完毕
        System.out.println("批量任务全部处理完成,主线程退出");
    }
}
3. 定时任务处理(Java 21+ 变通方案)

Java 21 暂未提供 ScheduledExecutorService 的虚拟线程实现,可通过'平台线程定时调度 + 虚拟线程执行任务'的方式实现定时任务的高并发处理:

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

public class VirtualThreadScheduledDemo {
    public static void main(String[] args) throws InterruptedException {
        // 1. 平台线程池(用于定时调度,仅需少量线程)
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        
        // 2. 虚拟线程池(用于执行实际定时任务,支撑高并发)
        try (var taskExecutor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 3. 定时任务:每 1 秒执行一次,延迟 1 秒启动
            scheduler.scheduleAtFixedRate(() -> {
                // 提交任务到虚拟线程池执行
                taskExecutor.submit(() -> {
                    System.out.printf("定时任务执行中,时间:%s,虚拟线程名称:%s%n", 
                        System.currentTimeMillis(), Thread.currentThread().getName());
                    try {
                        TimeUnit.MILLISECONDS.sleep(500); // 模拟任务执行
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            }, 1, 1, TimeUnit.SECONDS);
            
            // 主线程等待 10 秒后关闭定时调度
            TimeUnit.SECONDS.sleep(10);
            scheduler.shutdown();
            System.out.println("定时调度已关闭");
        }
        System.out.println("虚拟线程池已关闭,主线程退出");
    }
}

四、实战场景:Spring Boot 中使用虚拟线程

Spring Boot 3.2+ 正式支持虚拟线程,可轻松将 Web 服务、异步任务切换为虚拟线程,无需修改业务代码。

1. 环境准备
  • Spring Boot 版本:3.2.0 及以上
  • Java 版本:21 及以上

依赖配置(pom.xml):

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    <relativePath/>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
2. 配置 Web 服务使用虚拟线程

修改 application.properties 或 application.yml,将 Tomcat/Jetty 的线程池切换为虚拟线程:

# application.yml
spring:
  # 配置 Tomcat 使用虚拟线程
  tomcat:
    threads:
      virtual: true
  # 或配置 Jetty 使用虚拟线程(若使用 Jetty 容器)
  # jetty:
  #   threads:
  #     virtual: true
3. 业务代码示例(无需修改,直接受益于虚拟线程)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;

@SpringBootApplication
@RestController
public class SpringBootVirtualThreadDemo {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootVirtualThreadDemo.class, args);
    }

    // 高并发接口,自动使用虚拟线程处理
    @GetMapping("/api/hello")
    public String hello() throws InterruptedException {
        // 模拟 I/O 阻塞(如数据库查询、RPC 调用)
        TimeUnit.MILLISECONDS.sleep(200);
        return String.format("Hello, Virtual Thread! 线程名称:%s,是否为虚拟线程:%b", 
            Thread.currentThread().getName(), Thread.currentThread().isVirtual());
    }
}
4. 异步任务使用虚拟线程

通过 @Async 注解结合虚拟线程池,实现异步任务的高并发处理:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

@SpringBootApplication
@EnableAsync // 开启异步注解支持
@RestController
public class SpringBootVirtualThreadAsyncDemo {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootVirtualThreadAsyncDemo.class, args);
    }

    // 配置虚拟线程池作为异步任务执行器
    @Bean("virtualThreadExecutor")
    public Executor virtualThreadExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置任务提交时使用虚拟线程
        executor.setThreadFactory(Thread.ofVirtual().name("async-virtual-thread-").factory());
        executor.setTaskDecorator(runnable -> runnable);
        executor.initialize();
        return executor;
    }

    // 异步方法,使用虚拟线程池执行
    @Async("virtualThreadExecutor")
    public void asyncTask() throws InterruptedException {
        System.out.printf("异步任务执行中,线程名称:%s,是否为虚拟线程:%b%n", 
            Thread.currentThread().getName(), Thread.currentThread().isVirtual());
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.println("异步任务执行完成");
    }

    // 调用异步任务的接口
    @GetMapping("/api/async")
    public String async() throws InterruptedException {
        asyncTask();
        return "异步任务已提交";
    }
}

五、使用注意事项

  1. 避免 CPU 密集型任务:虚拟线程的优势是处理阻塞式 I/O 任务,CPU 密集型任务(如大数据计算、加密解密)应使用平台线程池(线程数 = CPU 核心数 / 核心数 * 2),避免虚拟线程切换开销抵消性能收益。
  2. 谨慎使用 ThreadLocal:虚拟线程数量可轻松达到百万级,若每个虚拟线程通过 ThreadLocal 存储大对象,会导致内存急剧膨胀;同时,虚拟线程复用可能引发 ThreadLocal 泄漏,建议使用 InheritableThreadLocal(仅适用于父子线程传递)或避免在虚拟线程中大量使用 ThreadLocal。
  3. 不要手动管理虚拟线程生命周期:虚拟线程无需手动调用 join()、interrupt()(除非特殊场景),由 JVM 自动管理,手动干预可能导致资源泄漏。
  4. 避免长时间持有锁:虚拟线程高并发下,长时间持有锁(如 synchronized、ReentrantLock)会加剧锁竞争,导致载体线程阻塞,建议使用无锁编程(CAS)或细粒度锁。
  5. 调试与监控:虚拟线程支持传统调试工具(IDEA、Jstack),使用 jstack <pid> 可查看虚拟线程状态(标记为 VirtualThread);监控工具(Prometheus、SkyWalking)需升级到支持 Java 21 的版本,确保能正确采集虚拟线程指标。
  6. 资源限制:虽然虚拟线程开销低,但仍需避免无限制提交任务(如百万级任务同时提交),可通过限流组件(如 Sentinel)控制任务提交速率,防止业务逻辑异常导致的任务积压。

六、总结

  1. 核心创建方式:快速使用用 Thread.startVirtualThread(),自定义属性用 Thread.ofVirtual(),有返回值任务用 Executors.newVirtualThreadPerTaskExecutor()+Future。
  2. 线程池首选:实际开发优先使用 Executors.newVirtualThreadPerTaskExecutor() 虚拟线程池,无需配置线程数,支持自动关闭。
  3. 实战落地:Spring Boot 3.2 + 可通过简单配置实现 Web 服务和异步任务的虚拟线程改造,无需修改业务代码。
  4. 使用边界:仅适用于阻塞式 I/O 任务、高并发轻量任务,避免 CPU 密集型任务和过度使用 ThreadLocal。

目录

  1. 一、使用前提
  2. 编译
  3. 运行
  4. 预期输出(示例):openjdk version "21.0.1" 2023-10-17 LTS
  5. 二、基础用法:创建与启动虚拟线程
  6. 1. 方式 1:直接启动(静态方法快捷创建)
  7. 2. 方式 2:手动创建 + 启动(灵活配置线程属性)
  8. 3. 方式 3:通过 Future 获取执行结果(有返回值任务)
  9. 三、进阶用法:虚拟线程池的使用
  10. 1. 核心特性
  11. 2. 批量任务处理示例
  12. 3. 定时任务处理(Java 21+ 变通方案)
  13. 四、实战场景:Spring Boot 中使用虚拟线程
  14. 1. 环境准备
  15. 2. 配置 Web 服务使用虚拟线程
  16. application.yml
  17. 配置 Tomcat 使用虚拟线程
  18. 或配置 Jetty 使用虚拟线程(若使用 Jetty 容器)
  19. jetty:
  20. threads:
  21. virtual: true
  22. 3. 业务代码示例(无需修改,直接受益于虚拟线程)
  23. 4. 异步任务使用虚拟线程
  24. 五、使用注意事项
  25. 六、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Gazebo 仿真环境构建与优化实践
  • C++ 笔试算法实战:打怪模拟、字符串分类与连通分量
  • 黑客、骇客、红客、蓝客与飞客的定义及区别解析
  • FL-NAS: 基于大语言模型的资源受限设备 NAS 公平性研究
  • 基于 Java + SpringBoot + Vue 的图书大厦管理系统设计与实现
  • Python 基于 DXGI 实现现代游戏窗口无闪烁截图方案
  • 动态规划 01 背包问题详解与空间优化
  • 网页开发常用知识点梳理
  • AIGC 赋能 Java 编程:智能工具提升开发效率与质量
  • Android WebView 版本升级方案详解
  • Nginx 安装、配置与常用功能实战指南
  • 红客联盟历史回顾与网络安全技术入门
  • OpenClaw 配置 GLM-4.7 Flash 与 DuckDuckGo 搭建飞书联网机器人
  • AI 绘画在商业设计中的应用与版权探讨
  • Windows 本地部署 OpenClaw 集成 DeepSeek 与飞书实现 AI 控制
  • OpenClaw SOUL.md:用自然语言定义 AI Agent 的身份与边界
  • AI 大模型通信机制解析:流式传输与数据封装逻辑
  • 国内 Stable Diffusion 与 LLaMA 模型镜像站推荐及训练实践
  • JDK 安装与环境配置完整指南
  • LeetCode 75. 颜色分类

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online