虚拟线程杀死了Java养Python AI
我昨天遇到一件很扎心的事。
一家做了八年Java的电商公司,架构师跟我倒苦水:他们的AI团队三年前为了让推荐模型上线,硬生生在Spring Boot旁边又起了一套Python微服务栈——FastAPI + Celery + Redis + 几台GPU推理节点。三年下来,这套旁路栈吞掉了近四百万的运维预算,团队里同时养着Java和Python两套发布流水线、两套监控、两套告警、两套CVE补丁流程。每次模型升级都要在两套语言之间做协议胶水,定位一个跨栈调用超时的问题平均要花半天,on-call工程师经常在凌晨被叫起来排查FastAPI和Spring Cloud Gateway之间的证书过期问题。
我问他:为什么不合并回Java?
他愣了五秒说:因为Java调LLM会阻塞线程啊。
这句话在2024年是对的,在2025年勉强还对,在2026年7月,已经完全错了。理由是:那个"Java养Python AI"的故事,底层的性能账、体验账、基础设施账,三本账在最近十二个月里被同时翻盘了。
那个流传五年的"Java调AI会卡死",是一笔糊涂账
这个误解的根源很简单:传统Java里一个HTTP请求线程对应一个OS线程,OS线程占大约1MB栈空间。LLM调用动辄300到2000毫秒,一个线程被LLM卡住,OS线程就真真切切被占住几十秒。如果你要支撑一万个并发LLM调用,光线程栈就要吃掉10GB内存。所以"Java不适合AI"的说法,本质是"传统Java线程模型不适合I/O密集型AI调用"——这个推理的每一步都是对的,唯一的错是把它当成"Java的固有缺陷"。
但这个前提在JDK 21已经不存在了。虚拟线程(Virtual Thread)让JVM在少量OS carrier线程上调度上百万个轻量级线程,一个虚拟线程等LLM响应时,JVM立刻把它park掉,OS线程立刻去跑别的虚拟线程。整个过程零OS上下文切换、零回调地狱、零反应式编程。你写的是干干净净的同步代码:
@GetMapping("/recommend") public List<Product> recommend(@RequestParam String userId) throws Exception { // 看起来是同步阻塞,但实际不占OS线程 String context = vectorStore.search(userId).get(); return chatClient.prompt() .system("你是推荐引擎") .user("用户ID:" + userId + ",历史:" + context) .call() .entity(List.class); }
这段代码在十年前是性能灾难,在今天能在8核机器上撑住上万个并发。没有任何反应式框架、没有回调链、没有Mono/Flux,Java工程师用最熟悉的"一步一步往下写"就能拿到并发红利。性能层面的借口在Spring Boot 3.2默认启用虚拟线程之后基本被堵死了。
到这里还没完。上一代Java AI服务为了绕开阻塞模型,不得不用WebFlux + R2DBC + 各种reactive适配器,最后发现响应式编程的学习曲线和bug率反而是新的负担。虚拟线程让大家回到最朴素的"同步代码 = 好维护"的开发哲学。Vlad Mihalcea做过一个对比:在相同并发下,虚拟线程版的Spring Boot比WebFlux版的内存占用低40%,P99延迟低15%。这不是小幅优化,是"Java AI服务为何要单独绕一圈去Python"的根本性质疑。
反过来看,这也解释了为什么过去五年大量"AI团队必须用Python"的论调本质上是基础设施没跟上时代的临时妥协。Datadog 2026年的报告显示,Java服务在AI集成场景的占比从2023年的9%回升到了18%,反弹的全部动力来自虚拟线程的成熟。如果你的Java团队还卡在2022年的"必须上Python栈"思维里,大概率是被历史包袱锁住了——而不是被任何技术必要性逼的。
Spring AI 2.0 把Java的AI开发体验拉到了Python同一档
光有虚拟线程不够。如果Java调用LLM还是要靠手撸HTTP+JSON解析+重试+Token计算,那团队维护成本仍然比不过Python那几行requests+langchain。
Spring AI 2.0 GA在2026年6月12日发布之后,这个差距被彻底抹平了。它干了三件让Java团队能合上Python栈的事。
第一,ChatClient统一入口。过去大家混用ChatClient和ChatModel,工具调用、重试、记忆管理全靠手动塞。2.0里ChatClient.create(chatModel)是唯一正统入口,工具调用、Advisor拦截、ChatMemory、VectorStore全部变成可组合的链式调用。最关键的破坏性变化是:2.0里如果你的代码里还有`chatModel.call(prompt)`并依赖模型自己处理工具调用,工具不会执行了——必须走ChatClient。这意味着一个Java开发可以用和写Spring MVC一样的方式写AI服务,团队不需要再为"AI"单独培训一套Python工程师。
第二,@Tool注解把业务方法直接暴露给模型。原来在Python里你要写一堆function calling的schema定义,模型才知道能调什么。Spring AI 2.0里:
@Tool(description = "查询用户最近的订单") public List<Order> recentOrders(@ToolParam String userId) { return orderService.findRecent(userId); } ChatClient chat = ChatClient.create(chatModel); String answer = chat.prompt() .user("我最近买了什么?") .tools(new OrderTools()) .call() .content();
写到这里就和写普通Service没有任何区别。同样的能力在Python里要写三倍代码,外加一个schema版本管理噩梦。
第三,动态工具发现省token。在工具多(50+)的场景,2.0的新机制能省34%到64%的输入token——这直接打到AI服务的运营成本要害。原来为了token省钱你才需要Python栈里更精细的控制,现在Java栈里也有这能力了。
把这三件事放在一起看,Spring AI 2.0本质上完成了"AI工程的Java化"。过去Python生态靠LangChain、LlamaIndex、LangGraph这一套独立生态抢走了Java工程师的AI机会,现在Spring AI把同一套能力以注解和配置的形式重新交给Java——更准确地说,是把AI工程变成了Spring应用的一个新切面。这不是框架的胜利,是"AI必须用Python"这个伪命题的终点。
飞书知识库里那篇《Java事务机制深度解析》讲过:Spring用@Transactional把事务边界显式声明出来,让开发者不必关心底层连接的开关、提交时机、回滚触发条件,复杂性被"事务"这个抽象接管了。ChatClient这条链本质上就是一个"AI事务"——模型调用、工具执行、记忆读写、向量检索、安全过滤,每一步要么都成功,要么按Advisor定义的策略回滚(重新生成、降级、跳过)。Java工程师不需要在每一处手动try-catch重试,因为Advisor已经把"事务传播行为"封装好了。写AI和写数据库事务,思维模型是一回事。
MCP 7月28日新版落地,Java AI服务的"水电气"终于齐了
如果说Spring AI 2.0解决的是"Java能不能干AI",那MCP(Model Context Protocol)解决的是"Java AI服务能不能在生产长期运行"。
MCP被业界类比成"AI的USB-C",意思是统一了模型和工具/数据源之间的接口。Spring AI 1.1起就原生支持MCP——Java应用既能消费外部MCP Server的能力,也能把自己的业务方法暴露成MCP Server给其他Agent调用。这是Java企业级AI生态真正补齐"水电气"的一步。
但MCP在过去一年有个硬伤:协议本身是有状态的。Server必须维护session,这导致sticky路由、扩容受限、负载均衡不友好。生产部署里只有约5%的MCP Server真正跑在生产环境,其余95%还在开发者的笔记本电脑上。最大的拦路虎是认证——OAuth 2.1的token生命周期和长跑Agent会话对不上,企业级授权扩展虽然已经稳定,但生态接入还在早期。
3天之后的2026年7月28日,MCP 2026-07-28规范正式发布。这次的核心变化是把协议核心变成无状态的:
• 会话创建/恢复/迁移标准化,Server重启和扩容对客户端透明
• 资源读取引入TTL缓存控制(直接借鉴HTTP Cache-Control语义)
• 元数据层加入W3C Trace Context传播,分布式追踪可以从宿主应用一路跟到MCP Server再跟到下游调用
对Java团队的影响很直接:以前你要在Spring Cloud Gateway那一层自己做session sticky,现在交给MCP原生层就行;以前跨服务追踪靠Sleuth/Zipkin拼凑,现在MCP metadata里直接带traceparent header;以前缓存策略要自己写,现在工具结果可以像HTTP一样声明Cache-Control: max-age=60。
这些变化落到生产环境,意味着Java团队把"AI微服务"部署到K8s里时,扩缩容、HPA、链路追踪、缓存这些基础设施能力终于在AI场景里齐了。剩下唯一非基础设施的事就是写业务工具——这是Java工程师最擅长的事。
MCP 2026-07-28规范的另一个容易被忽略的细节是W3C Trace Context标准化。这对Java团队尤其重要,因为Spring Cloud Sleuth、Micrometer Tracing、OpenTelemetry这一整套链路追踪基建,在Spring生态里已经打磨了十年。MCP metadata层带traceparent header,意味着Java AI服务的调用链可以直接接入企业现有的Observability栈,不需要再为AI单独搭一套Zipkin或Tempo。这件事单独看很小,但是"AI和传统业务可观测性统一"在企业治理层面是非常硬的诉求。
飞书知识库里另一篇《Activiti:开源业务流程管理的革命性引擎》讲的是BPMN 2.0、流程变量、网关、排他/并行子流程。Agent Loop本质上就是工作流。ReAct的"思考→行动→观察→再思考"循环,Multi-Agent的Supervisor分发、并行竞速、条件路由——就是BPMN里的Service Task、Exclusive Gateway、Parallel Gateway。Spring AI 2.0的Graph框架是Activiti思想在AI时代的一次复刻。Java团队如果对Activiti/flowable/Camunda熟,那对Multi-Agent编排会有天然的肌肉记忆,不需要再去学LangGraph那一套Python专用抽象。
行动建议:先合并一个低风险场景,再决定是否拆Python栈
如果你的团队也有那个架构师的烦恼——一边Spring Boot一边Python AI——我的建议是三步走。
第一步,挑一个低风险场景做合并试点。比如内部知识库问答、SQL生成、合同摘要这种"调用LLM+查一个内部系统"的简单场景。用Spring AI 2.0 + 虚拟线程写一个MVP,让它和Python版并行跑两周,对比P99延迟、token成本、运维工单数、on-call负担。数字会替你说话。
第二步,如果数据支持迁移,把MCP Server化你最重要的三到五个业务工具,让其他AI Agent(包括非Java栈的)也能调用——这一步的ROI最高,因为你的业务方法被模型发现和调用的概率会显著上升。Anthropic、Microsoft、Okta、Asana、Atlassian、Figma、Slack这些公司已经在用Enterprise-Managed Authorization扩展做企业级MCP认证,Java企业级生态接入是早晚的事。
第三步,把虚拟线程+Spring AI 2.0的组合推广到所有高并发LLM调用场景,关掉Python AI服务的独立集群。省下的不只是机器,更是两套语言栈带来的认知成本——面试、培训、轮值、故障复盘,每一项都是隐性支出。
有一点要诚实说出来:合并Java栈不意味着把Python完全踢出公司。模型训练、数据科学、离线特征工程这些"靠近算法本身"的工作,Python仍然是最合适的工具。MCP的设计哲学就是让每种语言做自己最擅长的事——Java做企业级服务运行时,Python做模型和数据实验,Agent通过MCP在两者之间无缝切换。我们说的"拆Python AI微服务"特指那部分"调用LLM+查询业务系统"的胶水代码,这部分Java已经能做得更好。把工程师从胶水里解放出来,去做真正有差异化的算法工作——这才是合并栈的真正收益。
虚拟线程杀死了"Java养Python AI"在性能维度的最后一个理由。Spring AI 2.0把开发体验拉到了Python同一档。MCP 7月28日给Java AI服务补上了生产基础设施。剩下的,就是动手。Java工程师不需要转Python,他们只需要把自己过去十年积累的"事务思维"和"工作流思维"在AI时代复用一次而已。从今天开始,少养一套语言栈,把省下的精力还给真正有价值的事情。