本文摘要启用虚拟线程后接口吞吐不升反降根因是请求路径上的native方法调用导致pinning。用tracePinnedThreads定位后将文件I/O迁移到NIO API即可解决。一、问题与结论Spring Boot 3.2 JDK 21Tomcat 10.1.158 核容器200 并发压测。在application.yml中开启spring.threads.virtual.enabledtrue后QPS 不升反降P99 延迟上升实际并发度退化为 8CPU 核心数远低于平台线程模式下 Tomcat 默认的 200 并发。结论请求处理路径上存在new FileInputStream(path)调用触发 JDK 21 的 native 方法 pinning——虚拟线程被钉在载体线程上无法卸载。8 条载体线程全部被占满后调度器中排队的其余虚拟线程无法执行。虚拟线程的优势前提是阻塞时能被卸载pinning 使这个前提失效虚拟线程退化为每条载体线程只能同时处理一个任务反而不如平台线程。二、排查与根因定位方法启动参数加入 pinning 追踪java-Djdk.tracePinnedThreadsfull-jarapp.jarJDK 21 内置该检测机制full参数打印完整线程栈可定位到具体调用行。压测期间控制台持续输出 pinning 报告关键栈帧java.io.FileInputStream.readBytes(Native Method)Native Method标记说明 pinning 来自 native 方法调用而非synchronized锁——这与大量已发表的 synchronized pinning 文章不同本次触发点是一个看起来无害的文件读取。pinning 机制虚拟线程通过 ForkJoinPool 调度到载体线程执行并行度默认与 CPU 核心数相关。当虚拟线程执行Thread.sleep()、LockSupport.parkNanos()等阻塞操作时JVM 将其从载体线程卸载unmount载体线程转去执行其他虚拟线程。这是少量线程承载大量并发的核心机制。native 方法调用打断了这一机制native 代码直接在载体线程上执行JVM 无法在调用中途调度或中断载体线程被独占直到调用返回。FileInputStream.readBytes()、FileOutputStream.writeBytes()等均为 native 方法。JEP 491JDK 22只修复了synchronized场景的 pinning。native 方法的 pinning 是虚拟线程模型的固有限制升级 JDK 无法解决。I/O API 对比API底层实现触发 pinningnew FileInputStream(path)nativereadBytes()是Files.newInputStream(path)NIOFileChannel否new FileReader(path)包装FileInputStream是Files.newBufferedReader(path)NIOFileChannel否三、可运行示例最小复现环境JDK 21 Spring Boot 3.2.0。配置application.yml开启虚拟线程spring:threads:virtual:enabled:trueController 包含两个端点分别触发和规避 pinningpackagecom.example.demo;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.io.FileInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.nio.file.Files;importjava.nio.file.Path;importjava.util.Map;RestControllerpublicclassFileIOController{privatefinalPathtestFile;publicFileIOController()throwsIOException{this.testFileFiles.createTempFile(vt-test,.txt);Files.writeString(testFile,x.repeat(50000));}GetMapping(/pinned-read)publicMapString,ObjectpinnedRead()throwsIOException{try(FileInputStreamfisnewFileInputStream(testFile.toFile())){byte[]buffernewbyte[1024];while(fis.read(buffer)!-1){}}returnMap.of(api,FileInputStream,pinning,expected);}GetMapping(/unpinned-read)publicMapString,ObjectunpinnedRead()throwsIOException{try(InputStreamisFiles.newInputStream(testFile)){byte[]buffernewbyte[1024];while(is.read(buffer)!-1){}}returnMap.of(api,Files.newInputStream,pinning,none);}}编译运行并发起并发请求mvn clean package-DskipTestsjava-Djdk.tracePinnedThreadsfull-jartarget/demo.jarforiin$(seq1200);docurl-shttp://localhost:8080/pinned-read/dev/nulldone;wait四、验证结果与边界预期输出/pinned-read被请求后控制台打印 pinning 完整线程栈栈中可见java.io.FileInputStream.readBytes(Native Method)帧/unpinned-read请求后不出现任何 pinning 报告。实际输出200 并发请求/pinned-read时每次触发 pinning 报告/unpinned-read控制台无 pinning 输出。本地环境实测未做正式基准测试。常见失败替换为Files.newInputStream()后问题依旧。原因通常是第三方库模板引擎、序列化库内部仍调用FileInputStream。解决方法查看 pinning 栈的完整调用链确认来源若该库无法替换将该段文件 I/O 隔离到独立的平台线程池中执行。升级 JDK 22 不解决JEP 491 只修复synchronizedpinningnative 方法 pinning 依然存在。计划通过升级 JDK 一劳永逸解决 pinning 的团队对 native I/O 场景需要另找方案。何时不启用虚拟线程如果瓶颈在数据库查询耗时或 CPU 密集计算而非并发 I/O 等待虚拟线程不会带来收益反而增加排查 pinning 的成本。五、替代方案与取舍方案适用场景代价与边界迁移到 NIO API文件 I/O 可控能逐一替换调用点第三方库内部可能仍用FileInputStream需逐一排查文件 I/O 隔离到平台线程池无法替换的第三方库调用了 native I/O需额外管理线程池大小和生命周期不启用虚拟线程瓶颈在 CPU 计算或数据库查询不在 I/O 等待放弃虚拟线程的其他收益不应使用本文方案的情况文件 I/O 不在请求热路径上如仅启动时读取配置pinning 频率极低迁移成本大于收益。参考资料JEP 444 — Virtual ThreadsJEP 491 — Synchronize Virtual Threads without Pinning
企业数字化 ERP 产品动态
相关推荐
Linux进程线程 进程基础
进程是程序的执行过程,一个程序可以对应多个进程
程序是进程的”模板“,进程是程序的“运行态体现”
进程的执行过程本质是按程序指令逐步推进,程序的逻辑决定进程的行为
同一程序的多个进程共享程序的代码段(只读,到各自… · 2026/11/5 5:27:05
企业招聘FDE,真正考察的5项能力 FDE与AI原生组织变革系列 第4篇
作者:FDE中国社区 公众号:FDE前线
随着企业AI转型进入实际交付阶段,越来越多的招聘负责人开始关注FDE(Forward Deployed Engineer,前线部署工程师)这一岗位。但一个现实问… · 2026/11/5 5:27:05
ADS122U04+PIC18F46K42:高精度PT1000测温模块设计与实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 7:36:44
基于ADS122U04IPWR与STM32F765ZI的工业及HVAC高精度温度测量方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 7:36:44
ADS122U04+PIC32MZ:HVAC高精度温度采集系统设计与实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 7:36:44
ADS122U04IPWR+STM32F031C6工业温度测量方案解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 7:36:44
基于ADS122U04与PIC32MZ的高精度温度采集系统设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 7:36:44
ADS122U04与STM32F410RB工业温度采集方案:UART接口ADC实战详解 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 7:35:44
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38