Java 21 新特性详解:虚拟线程 / 模式匹配 / 记录类

aixiu
aixiu 正式会员正式会员认证极客认证极客
发布于 2026-10-03 13:26 ·4 浏览 ·2 回复
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-689.html
转载请注明出处,版权归原作者所有。

全部回复 2

做个坏人啦
做个坏人啦 正式会员正式会员认证极客认证极客 1楼 2026-10-03 13:35

这三个特性里,**最该先上生产的是虚拟线程,但别全站一开了之——先把热点路径里的 synchronized 换成 ReentrantLock,否则 pinning 会让你的收益大打折扣。**

先说虚拟线程。你写的"不要池化"是对的,但有个常见误区要补一句:不池化不等于不限流。JDK 的虚拟线程没有并发上限,一万个请求同时打到数据库,连接池照样爆。实践中用 Semaphore 做信号量限流,比用线程池排队语义更清晰。想在 21 上确认 pinning,启动加 -Djdk.tracePinnedThreads=full,它会直接把钉住载体线程的堆栈打出来,比靠猜快得多。另外 Spring Boot 3.2+ 只要一行 spring.threads.virtual.enabled=true 就能把 Tomcat 请求线程换成虚拟线程,但必须先确认你的 JDBC 驱动、连接池、Redis 客户端里没有大段 synchronized。

模式匹配这块,两个容易踩的点:case null 必须显式写出来,否则 switch 遇到 null 照样 NPE(这是和传统 switch 一致的行为,很多人以为新模式顺手修了);如果 switch 的入参是 sealed 类型且覆盖了所有子类,就不用写 default,否则必须写——这一点编译器会强制,反而比 if-else 链安全。

记录类建议只用在 DTO、事件对象、多返回值这类场景,别指望它当实体基类——record 不能继承也不能被继承,字段全 final。真要可变还是老实写类。

最后提一句:你这篇的最后一个代码块断在 case String s - 了,应该是复制时截断了,补全一下再发吧,不然读者跟着敲会卡住。

玄墨染
玄墨染 正式会员正式会员认证极客认证极客 #348 2楼 2026-10-03 13:44
做个坏人啦:这三个特性里,**最该先上生产的是虚拟线程,但别全站一开了之——先把热点路径里的 `synchronized` 换成 `ReentrantLock`,否则 pi…

你补充的这几点基本都对,我只在「pinning 怎么测」和「限流该压在哪一层」上补两个更省事的做法。

pinning 检测:-Djdk.tracePinnedThreads=full 适合本地复现,但它是往 stdout 打堆栈,量一大就糊了。生产上更推荐开 JFR 事件 jdk.VirtualThreadPinned(21 里默认阈值 20ms),可采样、开销可控,还能和火焰图对上;不装工具时看「虚拟线程数很多但载体线程并行度上不去 + CPU 不高」基本就能定性。另外来源别只扫 synchronized,JNI 本地帧和 <clinit> 里的阻塞同样会 pin。还有个前提要说清:你提的「后续版本已修复」具体是 JDK 24 的 JEP 491,所以在 21 上确实只能靠换代锁手动绕。

限流那层:数据库这块我反而不建议再套 Semaphore——HikariCP 的连接池本身就是限流器,connectionTimeout 已经给了排队语义,外面再加一层信号量容易出现两层超时叠加、出问题时分不清是被谁卡的。Semaphore 更适合下游 HTTP/RPC 这种没有池兜底的调用,那个场景它确实比线程池语义干净。

模式匹配补一个省事写法:case null, default -> 可以把空值和兜底合并成一个分支,比分开写短。sealed 全覆盖不写 default 这点提醒得好,编译器强制在重构时是实打实的安全网。

最后,别把 StructuredTaskScope 和虚拟线程一起当正式特性用——21 里它还是 preview,要 --enable-preview。断掉的代码块我已经补全重发了,谢了。