写Java并发代码的人,今年都在聊StructuredTaskScope。fork-join模型干净、虚拟线程便宜,看起来所有异步问题都有了标准答案。
直到你遇到一个需要等支付确认的步骤。
![]()
不是等几毫秒,不是等下游服务回个HTTP 200。是彻底让出执行,把状态落盘,等一个可能几小时后、甚至JVM重启之后才来的外部事件。这时候你会发现:STS的结构化,是空间上的结构化——所有fork的任务绑定在一个活着的调用栈上。它不负责时间。
这是Exeris Kernel系列第五篇。前面四篇铺了很长的路:用ScopedValue换掉ThreadLocal,论证某些层不需要运行时多态,把TLS彻底踢出堆外。这一篇讲STS在哪里停住,以及必须造什么来补位。
压力来自具体的场景。我在做Kernel的编排层——带补偿逻辑、能接外部事件的多步骤业务流程。先看了一圈现成的:Camunda、Axon,还有同类企业级saga框架。全否了。它们要单独跑一个进程,有自己的JVM、内存 footprint、生命周期。Kernel的其他约束——零分配热路径、ScopedValue透传、堆外状态——也塞不进去。
于是往下沉一层。STS起初看起来是天然选择。fork工作,join结果,生命周期有保证。Loom让这很便宜。
然后撞上那个支付确认的步骤。
边界在这里划清:一旦执行边界需要跨越时间而非作用域,你就彻底离开了STS的地盘。这不是批评——STS没有被替换。Kernel里现在还有几处用它,而且用法跟入门教程不太一样。
第一处是直观的。单个Flow步骤内部,一个动作要调两个独立服务、合并结果再返回FlowOutcome.CONTINUE。执行绑在这个步骤的虚拟线程上,STS管fan-out,Flow管步骤边界。标准结构化并发。
第二处在L3,Events子系统里。InMemoryEventBus.publishAndAwait开个scope,每个注册handler fork一个虚拟线程,join,等全完再返回。CommunityEventLoop.dispatchBatch也是这个形状,节奏不同:每批drained的事件,每个fork一个VT。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.