前言

  • 这篇文章很短,问题也不大,但确实让我小小地开窍了一下= =

背景

  • 去年开始随着业务不断更新迭代,老应用的💩山越堆越多,并且随着人员不断变动,代码变得越来越难维护了

  • 后来开发团队终于对老应用进行新应用拆分 + 部分重构,为了尽可能的少留下历史包袱,决定新应用统一升级到JDK21

  • 既然升级到JDK21自然就是要用到其重磅的虚拟线程特性,但就在众人都以为性能咔咔猛涨的时候,压测把机器压挂了🤣


问题现象 & 原因

  • 这里就直接说结论了:

    • 原因就是将原本的线程池改为虚拟线程池后,直接使用Executors.newVirtualThreadPerTaskExecutor() ,没有进行池化,并且单个线程中有个不小的对象,随着qps的提升没过几十秒就oom了

    • 内存快照如下:

      • 2026-03-08-olsdjkzd.png
  • 那当时负责迁移的人,为什么就直接使用了Executors.newVirtualThreadPerTaskExecutor() 呢?虽然这是官方推荐的写法,但正常有点开发经验的人都应该知道线程要池化才对,甚至问题出现后的补救措施里说要使用信号量控制线程池数量:

  • 信号量确实在某些场景比较常用,但这种情况下用在控制线程数量着实是让我觉得有些违和,而这种奇葩"绕路"的写法我好像不止一次见过?思索了一阵子后我很快就想到了一个可能,这段代码极有可能是当时的开发让AI把老的线程池"翻译 or 续写"成JDK21的虚拟线程出来的

  • 为了验证这个想法,我试着把这个问题输入到内网的前文和豆包,一看回答,果然如此(2026-03验证没出现这样的错误了):

    • 内网千问的回答

    • 豆包的回答

      • 2026-03-08-ruxpgmcl.png
  • 这下基本可以确定是AI幻觉搞出来的鬼了,当时网络上公开的资料 & 应用虚拟线程的代码里,确实极少有提到正确池化姿势,但实际上你但凡对着虚拟线程的接口. 一下,就能看到创建池化工厂的接口了:

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1000,
                1000,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000),
                Thread.ofVirtual().name("Xxx-pool").factory(), // 改动这里即可
                new ThreadPoolExecutor.AbortPolicy()
        );

思考

这里的有些话并不是我自己悟出来的,而是之前看过的某篇文章有说过,但现在又找不到了,等之后翻下2月份浏览记录找到再补贴上来

  • 上面的问题应该发生吗?我觉得不应该:

    • 如果只有一篇Java官方文档推荐你直接使用Executors.newVirtualThreadPerTaskExecutor() 再加上AI的胡乱回答我觉得这也无可厚非

    • 但实际上在内网Ali JDK升级21的文档中,就有明确指出不推荐使用Executors.newVirtualThreadPerTaskExecutor() ,并且给出了oom的demo(之前升工具应用到21的时候我读过):

      • 2026-03-08-mlcwoqxl.png
    • 由此可以看出,这个问题发生的根本原因,是那位开发的思维已经有点倾向于"AI给的就是最优解",并且一定程度上丢失了对代码的"敏感度"


  • 回想一下过去一年,类似上面的问题其实出过不少例,有因为用AI编写完离线SQL不检查而导致线上事故的、有因为AI不懂内部某个中间件强行续写导致配置项混乱、也有一些新人让AI写完代码后出现了AI也解决不了错误后只能干瞪眼的......我在这里举这些case并不是想嘲讽,实际上也有"超级单兵"光是靠AI + Vibe Coding就一个人复刻了各大厂商的app,很多人也在AI帮助下跨出了以前难以跨出的步伐,比如让后端写前端

  • 过去有句古话叫「Talk is cheap, show me the code」,然而在现在看来,未来里这句话很可能会变成「Code is cheap, show me the prompts」 。上面的问题是不应该发生,但这同时也反映了一个趋势,随着AI对代码认知的不断提升,越来越多的编码工作会交给AI,而这也必然伴随着部分人代码能力的退化,未来所谓的CPP、Java、Golang、甚至近几年才兴起的Rust都可能变成现在的"汇编语言",只有"专业人员"才需要精通,普通的开发可能了解一下就够了

  • 最后dream一下,这可能是一个分歧点:

    • 成为代码能力退化的"超级单兵",虽然不会自己从0到1用编程语言实现,但是有一手好的prompts,在诸如新的spec-kit框架下全栈都能开发且效率1个顶10个,产品-开发-测试-上线-创业-破产一条龙

    • 成为依旧保持"旧时代"能力顶尖的"专业人士",在AI遇到无法解决的困难时横扫报错,并且能给AI注入许多新能力,充当人与AI协作之间的润滑剂

    • 全都要(被业务折腾怕了,反正我是很难有这种精力了)