这是Java的Bug还是我误用了Java(预览版)的结构化任务范围?
问题:Java 22结构化任务范围结合ScopedValue触发StructureViolationException
以下是从大型项目中提取的最小复现代码,使用Java 22(预览版)的结构化任务范围与虚拟线程,运行时抛出异常,请问这是Java的Bug还是API误用?
playground.java
void main() throws InterruptedException { final var NAME = ScopedValue.<String>newInstance(); try (var ts = new StructuredTaskScope<>()) { ScopedValue.runWhere(NAME, "haha", () -> { ts.fork(() -> { // ^^^ // java.util.concurrent.StructureViolationException: Scoped value bindings have changed return null; }); }); ts.join(); } }
运行命令:java --enable-preview --source 22 playground.java
解答
这是API误用,不是Java的Bug。核心原因如下:
StructuredTaskScope的设计规则:调用fork()方法的线程,其ScopedValue绑定必须与创建该任务范围时的绑定完全一致。- 你的代码中,
ts是在未绑定NAME的上下文里创建的,但调用ts.fork()时,线程已经通过ScopedValue.runWhere()修改了绑定状态,导致上下文不匹配,触发StructureViolationException。
正确写法示例
方式一:在fork的任务内部绑定ScopedValue
将ScopedValue的绑定逻辑放在fork出的任务中,确保任务继承任务范围的上下文后再修改绑定:
void main() throws InterruptedException { final var NAME = ScopedValue.<String>newInstance(); try (var ts = new StructuredTaskScope<>()) { ts.fork(() -> { return ScopedValue.runWhere(NAME, "haha", () -> { // 在此执行需要使用NAME绑定值的逻辑 return null; }); }); ts.join(); } }
方式二:先绑定ScopedValue再创建任务范围
如果需要主线程先绑定ScopedValue再管理任务范围,需在绑定上下文内创建StructuredTaskScope:
void main() throws InterruptedException { final var NAME = ScopedValue.<String>newInstance(); ScopedValue.runWhere(NAME, "haha", () -> { try (var ts = new StructuredTaskScope<>()) { ts.fork(() -> { // 此处可直接访问NAME的绑定值"haha" return null; }); ts.join(); } }); }
内容的提问来源于stack exchange,提问作者Salathiel Genese
相关产品推荐
相关产品推荐

