Это ошибка Java или я неправильно использую структурированную область задач Java (предварительная версия)?

Итак, у меня есть этот отрывок кода (минимальное воспроизведение из гораздо более крупного проекта и проблема с блокировкой). Он использует структурированную область задач 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.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
0
135
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы неправильно используете структурированную область задач.

В 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();
  }
}

Пожалуйста, не используйте «по моему скромному мнению», когда все это весьма объективно и совсем не субъективно. Ваш код правильный, код ОП неправильный. Они спрашивают о SO, я не думаю, что они против создания впечатления, что в их коде есть некоторые проблемы :) — Если не считать этой отягчающей оплошности стиля, отличный ответ!

rzwitserloot 11.06.2024 14:41

Конечно, вы также можете создавать новые области внутри подобласти, то есть вызывать ScopedValue.runWhere(…) внутри разветвленной задачи. Единственное, чего вы не можете сделать, — это разветвить задачу внешней области внутри внутренней области.

Holger 11.06.2024 15:05

Или, чтобы показать, что это также работает с одним 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(); }

Holger 12.06.2024 13:20

Принятый ответ адресован моему вопросу для понимания, но я застрял на другом... Потому что значение, которое я хочу определить, - это не String, а конкретное StructuredTaskScope (чтобы вложенные потоки могли разветвляться в пределах одной и той же области задачи).

Salathiel Genese 12.06.2024 16:10

@SalathielGenese Я не понимаю, что вы имеете в виду, но именно это StructuredTaskScope. Я не думаю, что можно хранить StructuredTaskScope в ScopedValue. Но вы можете либо передать параметр StructuredTaskScope как параметр другим методам, либо создать вложенные объекты StructuredTaskScope (см. второй пример в моем ответе). Если вам нужно что-то еще, задайте новый вопрос с примером того, что вы хотите.

Thomas Kläger 13.06.2024 08:00

Спасибо @ThomasKläger за продолжение. Это заняло у меня некоторое время, но я обратился к этому с тремя уровнями косвенных указаний. 1. ограниченное значение — это функция, соответствующая методу fork. 2. внешняя область структурированной задачи (STS) устанавливает значение этой функции в атомарную ссылку STS. 3. Значение этой атомарной ссылки устанавливается на внутренний STS. Это касалось моей проблемы. Еще раз спасибо, Томас.

Salathiel Genese 13.06.2024 08:05

@SalathielGenese весь смысл StructuredTaskScope заключается в поддержке четко вложенных областей задач. Мне неясно, какую выгоду вы ожидаете получить от внедрения сценария, который в это не вписывается. Для вашего сценария результат будет не лучше, чем при использовании, например. Executors.newVirtualThreadPerTaskExecutor() в первую очередь.

Holger 13.06.2024 11:01

@Хольгер, я согласен. Я реализую API Java Flow, который должен передавать TCK реактивного потока. Я пытаюсь создать его резервную копию виртуальными потоками. НО... Я хочу, чтобы API был разработан как RxJS (а не RxJava или Project Reactor, которые имеют ограничения на пользовательские операторы). ДАЛЬШЕ... Я узнал, что некоторые операторы, такие как FlatMap, могут запустить своего собственного производителя, и им потребуется запускать потоки в одной и той же области структурированных задач (STS). НАКОНЕЦ, именно здесь значения с ограниченной областью пригодятся для совместного использования значения потокобезопасным способом IoC PS: я хочу, чтобы был открыт метод fork, но не сам STS. imgur.com/carbon-r1cAbOS

Salathiel Genese 13.06.2024 14:41

Другие вопросы по теме