Итак, у меня есть этот отрывок кода (минимальное воспроизведение из гораздо более крупного проекта и проблема с блокировкой). Он использует структурированную область задач Java 22 (предварительная версия) в сочетании с виртуальными потоками:
игровая площадка.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 22, вы можете запустить его с помощью java --enable-preview --source 22 playground.java.




Вы неправильно используете структурированную область задач.
В JavaDoc для StructuredTaskScope.fork() указано:
StructureViolationExceptionPREVIEW — если текущие привязки значений области не такие же, как при создании области задачи.
Ваш код изменяет привязки значений области, вызывая ScopedValue.runWhere(NAME, "haha", () -> {}); после создания области задачи.
Вероятно, вам следует структурировать свой код следующим образом:
void main() throws InterruptedException {
final var NAME = ScopedValue.<String>newInstance();
ScopedValue.runWhere(NAME, "haha", () -> {
try (var ts = new StructuredTaskScope<>()) {
ts.fork(() -> {
return null;
});
});
ts.join();
}
}
Или вы можете создать новую область задачи после изменения значения области:
void main() throws InterruptedException {
final var NAME = ScopedValue.<String>newInstance();
try (var ts = new StructuredTaskScope<>()) {
ScopedValue.runWhere(NAME, "haha", () -> {
try (var ts2 = new StructuredTaskScope<>()) {
ts2.fork(() -> {
return null;
});
ts2.join();
}
});
ts.join();
}
}
Конечно, вы также можете создавать новые области внутри подобласти, то есть вызывать ScopedValue.runWhere(…) внутри разветвленной задачи. Единственное, чего вы не можете сделать, — это разветвить задачу внешней области внутри внутренней области.
Или, чтобы показать, что это также работает с одним StructuredTaskScope (где решающим моментом является изменение значения после разветвления внутри разветвленной подзадачи): try(var ts = new StructuredTaskScope<>()) { ts.fork(() -> ScopedValue.callWhere(NAME, "haha\n", () -> System.out.append(NAME.get()))); ts.fork(() -> ScopedValue.callWhere(NAME, "other\n", () -> System.out.append(NAME.get()))); ts.join(); }
Принятый ответ адресован моему вопросу для понимания, но я застрял на другом... Потому что значение, которое я хочу определить, - это не String, а конкретное StructuredTaskScope (чтобы вложенные потоки могли разветвляться в пределах одной и той же области задачи).
@SalathielGenese Я не понимаю, что вы имеете в виду, но именно это StructuredTaskScope. Я не думаю, что можно хранить StructuredTaskScope в ScopedValue. Но вы можете либо передать параметр StructuredTaskScope как параметр другим методам, либо создать вложенные объекты StructuredTaskScope (см. второй пример в моем ответе). Если вам нужно что-то еще, задайте новый вопрос с примером того, что вы хотите.
Спасибо @ThomasKläger за продолжение. Это заняло у меня некоторое время, но я обратился к этому с тремя уровнями косвенных указаний. 1. ограниченное значение — это функция, соответствующая методу fork. 2. внешняя область структурированной задачи (STS) устанавливает значение этой функции в атомарную ссылку STS. 3. Значение этой атомарной ссылки устанавливается на внутренний STS. Это касалось моей проблемы. Еще раз спасибо, Томас.
@SalathielGenese весь смысл StructuredTaskScope заключается в поддержке четко вложенных областей задач. Мне неясно, какую выгоду вы ожидаете получить от внедрения сценария, который в это не вписывается. Для вашего сценария результат будет не лучше, чем при использовании, например. Executors.newVirtualThreadPerTaskExecutor() в первую очередь.
@Хольгер, я согласен. Я реализую API Java Flow, который должен передавать TCK реактивного потока. Я пытаюсь создать его резервную копию виртуальными потоками. НО... Я хочу, чтобы API был разработан как RxJS (а не RxJava или Project Reactor, которые имеют ограничения на пользовательские операторы). ДАЛЬШЕ... Я узнал, что некоторые операторы, такие как FlatMap, могут запустить своего собственного производителя, и им потребуется запускать потоки в одной и той же области структурированных задач (STS). НАКОНЕЦ, именно здесь значения с ограниченной областью пригодятся для совместного использования значения потокобезопасным способом IoC PS: я хочу, чтобы был открыт метод fork, но не сам STS. imgur.com/carbon-r1cAbOS
Пожалуйста, не используйте «по моему скромному мнению», когда все это весьма объективно и совсем не субъективно. Ваш код правильный, код ОП неправильный. Они спрашивают о SO, я не думаю, что они против создания впечатления, что в их коде есть некоторые проблемы :) — Если не считать этой отягчающей оплошности стиля, отличный ответ!