—— Project Loom 带来的高并发新方式
一、为什么需要虚拟线程
1. 传统线程的限制
在 Java 中,线程是与操作系统线程(OS Thread)一一映射的:
每个线程需要 1MB 左右的栈内存,成千上万个线程会占用巨量内存;
OS 线程切换需要 上下文切换,开销大;
当线程因为 阻塞 I/O(例如网络请求、数据库访问)挂起时,它仍然占用一个宝贵的 OS 线程。
因此,传统线程适合 几百 ~ 几千 并发,而在 几万甚至几十万 并发场景下会遇到瓶颈。
2. 异步 / Reactive 模式的痛点
为解决并发问题,社区广泛采用了异步框架(如 CompletableFuture、RxJava、Reactor、Netty 等)。
但异步模式带来 代码复杂度 和 可读性差 的问题:
回调地狱(callback hell);
调试难度大,堆栈信息不直观;
新手不易上手。
3. Project Loom 的目标
Project Loom 提供了 虚拟线程(Virtual Threads):
由 JVM 管理的 轻量级线程;
一个 OS 线程可以挂载 / 调度成千上万个虚拟线程;
I/O 阻塞时,虚拟线程会挂起,OS 线程可去运行其他任务;
允许我们用 同步阻塞代码风格 编写高并发程序。
一句话总结:让高并发像写同步代码一样简单。
二、快速上手虚拟线程
1. 环境准备
JDK 21 或更高(LTS,支持虚拟线程)
启动时需开启预览特性:
javac --enable-preview --release 21 Main.java java --enable-preview Main
2. 创建虚拟线程
方式一:直接启动
Thread.startVirtualThread(() -> {
System.out.println("Hello from virtual thread!");
});
方式二:ExecutorService
import java.util.concurrent.*;
public class VirtualThreadDemo {
public static void main(String[] args) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
System.out.println("Task running in " + Thread.currentThread());
});
}
}
}
这里的 newVirtualThreadPerTaskExecutor()
相当于一个线程池,但为每个任务创建一个虚拟线程(几乎无成本)。
三、虚拟线程与平台线程对比
四、实战案例:高并发 HTTP 请求
假设我们要并发执行 5000 个 HTTP 请求。
传统线程池写法
ExecutorService pool = Executors.newFixedThreadPool(200);
for (int i = 0; i < 5000; i++) {
pool.submit(() -> {
fetch("https://httpbin.org/delay/1"); // 模拟耗时 I/O
});
}
pool.shutdown();
缺点:
线程数有限(200),其余任务要排队;
提高线程池大小 → 内存暴涨。
虚拟线程写法
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 5000; i++) {
executor.submit(() -> {
fetch("https://httpbin.org/delay/1");
});
}
executor.shutdown();
特点:
每个请求一个线程,代码同步直观;
JVM 自动调度,数千任务同时进行也没问题。
fetch()
方法实现示例:
static void fetch(String urlStr) {
try {
var url = new java.net.URL(urlStr);
var conn = (java.net.HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.getInputStream().readAllBytes(); // 模拟读取
System.out.println("Done: " + urlStr);
} catch (Exception e) {
System.err.println("Error: " + e);
}
}
五、性能实验步骤
你可以在本机做对比实验:
实验条件
测试任务数:5000
每个任务耗时 ~1s(使用 httpbin.org/delay/1 模拟)
对比:固定线程池(200 线程) vs 虚拟线程(5000 线程)
记录指标
总耗时(任务完成时间)
CPU 使用率
内存使用量
平均延迟
预期结果
固定线程池:任务分批执行,耗时 ~25s
虚拟线程:所有任务并发执行,耗时 ~1~2s
内存占用差异显著:平台线程几百 MB,虚拟线程几十 MB。
六、最佳实践
适用场景
I/O 密集型:数据库访问、HTTP 调用、RPC、文件 I/O。
高并发服务:网关、聊天服务器、微服务。
不推荐场景
CPU 密集型任务(虚拟线程不会比平台线程更快)。
使用某些 JNI 或依赖 OS 线程特性的库(需验证兼容性)。
与结构化并发结合
Loom 还引入 Structured Concurrency 概念,可以用StructuredTaskScope
来管理一组虚拟线程:try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<String> f1 = scope.fork(() -> fetch("https://a.com")); Future<String> f2 = scope.fork(() -> fetch("https://b.com")); scope.join(); // 等待任务完成 scope.throwIfFailed(); System.out.println(f1.resultNow() + f2.resultNow()); }
优点:让并发任务像代码块一样有生命周期,异常/超时能统一管理。
七、总结
虚拟线程 = 高并发的新利器,解决了 OS 线程昂贵、异步代码复杂的问题。
它让我们可以用 同步风格 写出 高并发、可读性高 的代码。
最佳应用场景:I/O 密集型、高并发 Web 服务。
在未来几年,虚拟线程有望成为 Java 服务端开发的“标配”。
默认评论
Halo系统提供的评论