Java 虚拟线程 Virtual Thread 让“每请求一线程”支持高并发

1.Virtual Thread 改变了什么?

先说重点:

1)Virtual Thread不是“新一代 ThreadPoolExecutor”,而是把原来那个沉重的 Thread 拆成了两层:

  • 底层少量 平台线程(OS 线程)
  • 上面跑着大量 虚拟线程(Virtual Thread)

2)对使用者来说,最大变化只有一句话:

开发者可以继续写“看起来是阻塞”的同步代码,但 JVM 会帮你用“协程式”的方式顶住高并发。

换句话说:

1)以前我们都知道:

“每请求一线程”写起来爽,但线程太重,撑不了多少并发。

2)有了虚拟线程之后,JVM 的态度变成了:

“你就放心每请求一线程吧,我把线程变轻一点。”

这会带来几个非常现实的好处:

1)写法不变 / 思维不变

  • Controller / Service 里继续用同步风格: userService.findUser(id)、orderService.listOrders(id),没有 Mono、CompletableFuture 满天飞。

2)并发能力上一个数量级

  • 原来你不敢把线程池开到几千、几万;
  • 现在用虚拟线程,几万、几十万“每请求一线程”都可以认真考虑。

3)大部分普通 Web / RPC 服务,没那么刚需 WebFlux/Netty 了

  • 不是说 WebFlux/Netty 没用了,而是:

终于可以用“最简单的写法”解决绝大多数高并发场景。

某位网友说过一句有点夸张又有点道理的话:

“一个 socket 对应一个虚拟线程就是最佳方案。”

这句话“最佳”二字有点吹过头,但有一点是对的:

在今天的 Java 里,“一个连接/请求一个虚拟线程”确实很可能是新的合理默认。

1. 回到老问题:为什么以前“每请求一线程”顶不住?

传统 Spring MVC / Tomcat 的模型,大概是这样:

  1. 客户端发来一个 HTTP 请求;
  2. Tomcat 从线程池里拿出一个 平台线程(OS 线程) 交给这个请求;
  3. 你的 Controller 里写一堆同步代码,里面各种阻塞调用:
@GetMapping("/user/{id}")
public UserProfile getUser(@PathVariable String id) {
    // 阻塞式 HTTP 调用
    User user = restTemplate.getForObject("http://user-service/" + id, User.class);
    // 阻塞式数据库调用
    List<Order> orders = jdbcTemplate.query("SELECT * FROM orders WHERE user_id = ?", rs -> {
        // ...
    }, id);
    return new UserProfile(user, orders);
}

看起来很优雅,对吧?

问题是:这个请求在等待下游服务的时候,那个 OS 线程也在那儿干等。

一个线程默认栈空间几 MB;线程多了以后:

  • 内存被吃光;
  • 上下文切换越来越频繁,CPU 时间都浪费在切线程上。

所以才会有这些“补救方案”:

1)调大 Tomcat 线程池:顶一阵,但迟早顶不住;

2)把阻塞调用改成异步:

  • Callable / DeferredResult / CompletableFuture / WebFlux;
  • 代码复杂度和心智负担一起上来。

抽象成一句话:

以前的“每请求一线程”模式,在高并发时代最大的问题就是:

线程太贵,阻塞太浪费。

于是大家才开始写:

  • sendAsync(...);
  • thenApply(...) / thenCompose(...);
  • Mono.zip(...) / flatMap(...)。

能顶住并发没错,但可读性、可维护性确实打折扣。

2. Virtual Thread 是什么?和普通 Thread 有啥不一样?

先用一句话把定义讲清楚:

平台线程(Platform Thread) ≈ OS 线程

虚拟线程(Virtual Thread) = JVM 自己调度的“用户态线程”

2.1 关键差异:谁来调度、谁在“阻塞”

传统 Thread:

一个 Thread 就是一个 OS 线程;

当你在里面调 socket.read()、jdbc、HttpClient.send():

  • 线程卡在内核里等待 I/O;
  • 整个 OS 线程被占住,啥也干不了。

虚拟线程:

虚拟线程自己并不直接对应 OS 线程;

它们是跑在少量 carrier threads(承载线程) 上的一堆“轻量任务”;

当虚拟线程遇到支持 Loom 的阻塞 I/O 时,JVM 会做两件事:

  1. 把这
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李景琰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值