话题动态
发起者
老王
话题状态
等待首个回复
参与人
1
互动热度
0
聊聊最近把订单履约链路迁移到 JDK 21 虚拟线程(Virtual Thread)踩到的坑,给大家提个醒,也想听听大家的解法。
先抛个结论:虚拟线程不是无脑替换原有线程池的银弹,特别是遇到旧代码里的 synchronized 时,甚至会导致吞吐量反向跳水。
上周我们灰度一个批处理接口,直接用了 Executors.newVirtualThreadPerTaskExecutor()。因果链是这样的:
某个调用链路深处调了老版本 SDK,底层有一个带 synchronized 的网络 I/O 阻塞调用 -> 触发了 Virtual Thread Pinning(钉死 Carrier Thread 无法 unmount)-> 载体线程池(ForkJoinPool)迅速被占满耗尽 -> 其他正常的虚拟线程全部排队饥饿 -> 最终接口 P99 从 80ms 直接飙到 3s+,表现比改动前传统线程池(Platform Thread)限流时还要惨烈。
排查时挂上 -Djdk.tracePinnedThreads=full 才抓到真凶,最后只能人肉把三方包里的锁改成 ReentrantLock。
顺便总结了两个目前我们团队的落地准则:
Semaphore 显式限流。synchronized 没事,但凡里面掺杂了 RPC、DB 查询、Redis 调用的,必须全部替换成 ReentrantLock。想问问已经在生产落地的大佬们: 你们在推虚拟线程时,遇到祖传代码或二方/三方 SDK(比如旧版 MySQL Driver、老 Apache HttpClient)里的 Pinning 隐患,是靠源码级替换解决,还是有更优雅的隔离手段?目前生产上大家把连接池、执行器彻底切换的比例大概有多少了?
全部回复(0)