首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深度剖析 Java 21 Project Loom:虚拟线程在高并发场景下的应用

深度剖析 Java 21 Project Loom:虚拟线程在高并发场景下的应用

作者头像
大熊计算机
发布2025-07-14 19:54:59
发布2025-07-14 19:54:59
30000
代码可运行
举报
文章被收录于专栏:C博文C博文
运行总次数:0
代码可运行

回顾 Java 传统线程模型的瓶颈及 Project Loom 的设计动机,接着深入剖析虚拟线程的原理、使用方式及调度机制,并结合多种高并发场景给出实战示例。

背景与动机

在传统 Java 并发模型中,每个 java.lang.Thread 都映射到一个操作系统线程(Platform Thread),线程数受限于 OS 资源,且单个线程阻塞时会占用整个 OS 线程,导致大并发场景下资源浪费和性能瓶颈。为了解决这一问题,OpenJDK 社区发起了 Project Loom,引入“虚拟线程”(Virtual Threads),以 JVM 层面的轻量级线程来实现大规模并发。

虚拟线程简介

什么是虚拟线程

虚拟线程是一种由 JVM 调度的轻量级线程类型,不再与 OS 线程一一对应,而是通过“载体线程”(Carrier Threads)复用底层操作系统线程资源。当虚拟线程被挂起(如遇到阻塞 I/O),它会释放载体线程,使该载体线程可以继续执行其他虚拟线程,从而极大地提升并发度和资源利用率。

JEP 425 与 JEP 444 概览
  • JEP 425: Virtual Threads (Preview) — 于 Java 19 引入预览,初步实现虚拟线程的 API 和运行时支持。
  • JEP 444: Virtual Threads — 于 Java 21 正式稳定,虚拟线程成为标准特性并优化了调度器与阻塞处理机制。

虚拟线程工作原理

Carrier Threads 与挂载机制

JVM 内部维护一组载体线程(Carrier Threads),用于实际执行虚拟线程的任务。虚拟线程在准备执行时“挂载”到某个载体线程,执行完毕或遇阻塞时“卸载”挂回队列,载体线程可立即切换到其他挂起的虚拟线程。

阻塞调用的处理

虚拟线程遇到阻塞调用(如网络 I/O、文件读写)时,JVM 会自动将该虚拟线程解除挂载,释放载体线程资源;待 I/O 就绪后,再次挂载并继续执行,用户无需显式使用异步回调或框架,从而简化编程模型。


在项目中使用虚拟线程

启用预览特性

pom.xml 或编译命令中加入 --enable-preview 并指定模块:

代码语言:javascript
代码运行次数:0
运行
复制
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <release>21</release>
    <compilerArgs>
      <arg>--enable-preview</arg>
      <arg>--add-modules</arg>
      <arg>jdk.incubator.concurrent</arg>
    </compilerArgs>
  </configuration>
</plugin>

同时在运行时添加 --enable-preview 参数。

创建与管理虚拟线程
方法一:Thread API
代码语言:javascript
代码运行次数:0
运行
复制
Thread vThread = Thread.ofVirtual().start(() -> {
    // 并发任务逻辑
});
vThread.join();
方法二:Executor API
代码语言:javascript
代码运行次数:0
运行
复制
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    List<Future<String>> futures = IntStream.range(0, 1000)
        .mapToObj(i -> executor.submit(() -> fetchData(i)))
        .collect(Collectors.toList());
    for (Future<String> f : futures) {
        System.out.println(f.get());
    }
}

以上两种方式均可在短代码路径内创建成千上万的虚拟线程。


高并发实战案例

HTTP 服务器模拟

使用 Java 内置的 HttpServer,采用虚拟线程处理每个连接:

代码语言:javascript
代码运行次数:0
运行
复制
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
server.createContext("/", exchange -> {
    Thread.sleep(10); // 模拟阻塞
    byte[] resp = "Hello, Loom!".getBytes();
    exchange.sendResponseHeaders(200, resp.length);
    exchange.getResponseBody().write(resp);
    exchange.close();
});
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
server.start();

在同一台 8 核机器上,虚拟线程模式下可轻松处理百万级并发连接,而平台线程模式往往在几万并发后 OOM。

数据库连接池优化

对于每个请求新建虚拟线程,自由开闭 JDBC 连接:

代码语言:javascript
代码运行次数:0
运行
复制
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> {
        try (Connection conn = ds.getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
            while (rs.next()) {
                // 处理数据
            }
        }
    });
}

虚拟线程释放载体线程时,底层 JDBC 驱动的阻塞调用不再阻塞主线程池,显著提升吞吐。


性能与对比

与平台线程的内存对比
  • 平台线程:每个线程默认保留 1 MB 堆栈;大并发下内存消耗高。
  • 虚拟线程:默认仅保留 ~1 KB 的轻量栈,实际按需分配,百万级线程内存消耗可控制在 几十 MB 以内。
与 Reactor/Netty 的编程复杂度对比

Reactor/Netty 等异步框架需使用回调或响应式流,链式编程模型较难调试;虚拟线程可保留同步编程风格,且无显式回调,易读易维护。


调度与限流策略

在高并发场景中,可结合 Semaphore、限流框架(如 Resilience4j)对虚拟线程并发量进行控制,防止依赖系统资源(数据库、API)过载。例如:

代码语言:javascript
代码运行次数:0
运行
复制
Semaphore semaphore = new Semaphore(100);
Executors.newVirtualThreadPerTaskExecutor().submit(() -> {
  if (semaphore.tryAcquire()) {
    try {
      // 业务处理
    } finally {
      semaphore.release();
    }
  } else {
    // 拒绝或降级逻辑
  }
});

与常见框架的集成

Spring Boot 中的虚拟线程支持

Spring Boot 3.2+ 可通过配置 TaskExecutor 使用虚拟线程:

代码语言:javascript
代码运行次数:0
运行
复制
@Bean
public TaskExecutor taskExecutor() {
    return new ConcurrentTaskExecutor(Executors.newVirtualThreadPerTaskExecutor());
}

@Async 方法、WebFlux 等均可无缝支持虚拟线程。

Quarkus 与虚拟线程

Quarkus 通过 @Blocking 注解结合虚拟线程执行阻塞操作,并可在 application.properties 中开启:

代码语言:javascript
代码运行次数:0
运行
复制
quarkus.virtual-threads.enabled=true

无需额外代码,即可获得虚拟线程优势。

最佳实践与注意事项

  1. 适用场景:I/O 密集型、高并发请求场景;CPU 绑定任务建议使用并行流或线程池。
  2. 线程本地存储:避免过度使用 ThreadLocal,推荐使用 Scoped Values(JEP 464)管理上下文。
  3. 调试与监控:使用 JFR(Java Flight Recorder)和 jstack 可查看虚拟线程状态;注意开启 -Djfr 支持。
  4. 逐步迁移:可先在次要模块开启虚拟线程,验证性能与稳定性后再全量推广。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景与动机
  • 虚拟线程简介
    • 什么是虚拟线程
    • JEP 425 与 JEP 444 概览
  • 虚拟线程工作原理
    • Carrier Threads 与挂载机制
    • 阻塞调用的处理
  • 在项目中使用虚拟线程
    • 启用预览特性
    • 创建与管理虚拟线程
      • 方法一:Thread API
      • 方法二:Executor API
  • 高并发实战案例
    • HTTP 服务器模拟
    • 数据库连接池优化
  • 性能与对比
    • 与平台线程的内存对比
    • 与 Reactor/Netty 的编程复杂度对比
  • 调度与限流策略
  • 与常见框架的集成
    • Spring Boot 中的虚拟线程支持
    • Quarkus 与虚拟线程
  • 最佳实践与注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档