Java 虚拟线程实战:用同步写法扛住十万并发
495 字
2 分钟
Java 虚拟线程实战:用同步写法扛住十万并发

为什么需要虚拟线程
传统的 Java 线程是对操作系统线程的一层薄封装,一个线程大约占用 1MB 栈空间,创建和切换成本都不低。于是我们才有了线程池、回调、CompletableFuture、响应式编程这一整套「为了省线程而牺牲可读性」的方案。
虚拟线程(Virtual Thread,JDK 21 正式)换了个思路:线程由 JVM 调度,成本极低,可以轻松创建几十万个。阻塞时,JVM 会把它从载体线程(carrier thread)上卸下来,让出 CPU。
结论:可以继续用最直观的同步代码,同时拿到接近异步的吞吐。
最小示例
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { List<Future<String>> futures = new ArrayList<>(); for (int i = 0; i < 100_000; i++) { int id = i; futures.add(executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return "task-" + id; })); } for (var f : futures) { f.get(); }}十万个任务各睡一秒,总耗时依然接近一秒。换成固定大小的平台线程池,你会等很久。
在 Spring Boot 中启用
Spring Boot 3.2+ 只需要一行配置:
spring: threads: virtual: enabled: trueTomcat 的请求处理线程、@Async 的执行器都会切换成虚拟线程。
常见误区
- 不要池化虚拟线程。 它们本来就是廉价的,用完即弃。需要限流时用
Semaphore,而不是缩小线程池。 - CPU 密集型任务没有收益。 虚拟线程解决的是「等待」问题,不是「计算」问题。
- 小心
synchronized造成的 pinning。 在 JDK 21 中,虚拟线程在synchronized块里阻塞会钉住载体线程。能换成ReentrantLock就换。可以用-Djdk.tracePinnedThreads=full排查。 - ThreadLocal 要克制。 线程数量暴涨后,每个线程一份 ThreadLocal 的内存开销会被放大。
小结
虚拟线程不是银弹,但对大多数 IO 密集的业务后端来说,它能让代码回到最朴素的样子。升级 JDK 21 之后,先把它开在非核心服务上压测一遍,数据会告诉你答案。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
Java 虚拟线程实战:用同步写法扛住十万并发
https://www.s9mc.cn/posts/java-virtual-threads/相关文章智能推荐
1
Redis 分布式锁:从 SETNX 到 Redisson 的坑
Java一把分布式锁要考虑的事情远比想象中多:原子性、过期时间、误删、续期。本文逐个拆解。
2
MySQL 索引与执行计划:从 EXPLAIN 看懂慢查询
Java学会读 EXPLAIN,理解最左前缀、覆盖索引与索引失效场景,让慢查询无处遁形。
3
Spring Boot 优雅停机:发布时不再丢请求
Java讲清楚 Spring Boot 的优雅停机机制,以及在 Kubernetes 与负载均衡下如何配合,做到发布零报错。
4
开了十二年服,我总结的 Paper 服务器调优清单
Minecraft从 JVM 参数到区块加载,从实体清理到定时任务,一份来自十二年运维经验的 Minecraft 服务器调优清单。
5
和 AI 结对编程:我的日常工作流
AI不是让 AI 替你写代码,而是让它成为你的搭档。分享我在日常开发中使用 AI 工具的几条原则与技巧。
随机文章随机推荐









