Класс StructuredTaskScope<T>
- Параметры типа:
-
T- тип результата задач, выполняемых в области задач
- Все реализованные интерфейсы:
AutoCloseable
- Непосредственные известные подклассы:
-
StructuredTaskScope.ShutdownOnFailurePREVIEW,StructuredTaskScope.ShutdownOnSuccessPREVIEW
public class StructuredTaskScope<T> extends Object implements AutoCloseable
StructuredTaskScope — это предварительная версия API платформы Java. StructuredTaskScope поддерживает случаи, когда задача разделяется на несколько конкурирующих подзадач, и где подзадачи должны завершиться перед продолжением основной задачи. Объект StructuredTaskScope может быть использован для обеспечения того, что жизненный цикл конкурирующей операции ограничен блоком синтаксиса, точно так же, как и последовательной операции в структурированном программировании. Базовая операция
ОбъектStructuredTaskScope создаётся одним из его публичных конструкторов. Он определяет метод fork для запуска потока для выполнения подзадачи, метод join для ожидания завершения всех подзадач и метод close для закрытия области задачи. API предназначен для использования со выражением
try-with-resources. Цель заключается в том, чтобы код в блоке try использовал метод fork для создания потоков для выполнения подзадач, ожидал завершения подзадач с помощью метода join и затем обрабатывал результаты. Вызов метода fork возвращает SubtaskПРЕВЬЮ для представления запущенной подзадачи. После вызова join можно использовать метод Subtask для получения результата успешного завершения или исключения, если подзадача завершилась неудачно. Callable<String> task1 = ...
Callable<Integer> task2 = ...
try (var scope = new StructuredTaskScope<Object>()) {
Subtask<String> subtask1 = scope.fork(task1);
Subtask<Integer> subtask2 = scope.fork(task2);
scope.join();
... process results/exceptions ...
} // close
В следующем примере создаётся набор однородных подзадач, ожидают завершения всех из них с помощью метода join и используются метод Subtask.StateПРЕВЬЮ для разделения подзадач на набор подзадач, завершившихся успешно, и набор подзадач, завершившихся неудачно.
List<Callable<String>> callables = ...
try (var scope = new StructuredTaskScope<String>()) {
List<Subtask<String>> subtasks = callables.stream().map(scope::fork).toList();
scope.join();
Map<Boolean, Set<Subtask<String>>> map = subtasks.stream()
.collect(Collectors.partitioningBy(h -> h.state() == Subtask.State.SUCCESS,
Collectors.toSet()));
} // close
Для обеспечения корректного использования, методы join и close могут быть вызваны только владельцем (потоком, который открыл/создал область задачи), и метод close выбрасывает исключение после закрытия, если владелец не вызвал метод join после создания подзадач.
StructuredTaskScope определяет метод shutdown для остановки области задачи без её закрытия. Метод shutdown() прерывает все незавершенные подзадачи, прерывая потоки. Он предотвращает запуск новых потоков в области задачи. Если владелец ожидает в методе join, он проснётся.
Остановка используется для прерывания и позволяет подклассам реализовывать политику, которая не требует завершения всех подзадач.
Подклассы с политиками для общих случаев
Определены два подклассаStructuredTaskScope для реализации политики в общих случаях: -
ShutdownOnSuccessПРЕВЬЮ захватывает результат первой подзадачи, завершившейся успешно. После захвата он останавливает область задачи, прерывает незавершенные потоки и разбуживает владельца. Этот класс предназначен для случаев, когда результат любой подзадачи подойдёт ("вызвать любую"), и нет необходимости ожидать результатов других незавершенных подзадач. Он определяет методы для получения первого результата или выбрасывает исключение, если все подзадачи завершатся неудачно. -
ShutdownOnFailureПРЕВЬЮ захватывает исключение первой подзадачи, завершившейся неудачно. После захвата он останавливает область задачи, прерывает незавершенные потоки и разбуживает владельца. Этот класс предназначен для случаев, когда результаты всех подзадач необходимы ("вызвать все"); если любая подзадача завершится неудачно, результаты других незавершенных подзадач больше не нужны. Он определяет методы для выбрасывания исключения, если любая из подзадач завершится неудачно.
Ниже приведены два примера, использующих эти два класса. В обоих случаях создаётся пара подзадач для получения ресурсов из двух URL-местоположений "left" и "right". Первый пример создаёт объект ShutdownOnSuccess для захвата результата первой подзадачи, завершившейся успешно, прерывая другие посредством остановки области задачи. Главная задача ожидает в join до тех пор, пока либо подзадача завершится с результатом, либо обе подзадачи завершатся неудачно. Он вызывает метод result(Function)ПРЕВЬЮ для получения захваченного результата. Если обе подзадачи завершатся неудачно, этот метод выбрасывает WebApplicationException с исключением одной из подзадач в качестве причины.
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
scope.fork(() -> fetch(left));
scope.fork(() -> fetch(right));
scope.join();
String result = scope.result(e -> new WebApplicationException(e));
...
}
joinUntil(Instant) до тех пор, пока обе подзадачи завершатся с результатом, либо завершатся неудачно, либо будет достигнут срок. Он вызывает throwIfFailed(Function)ПРЕВЬЮ для выбрасывания исключения, если любая подзадача завершится неудачно. Этот метод является пустым, если обе подзадачи завершатся успешно. Пример использует Supplier.get() для получения результата каждой подзадачи. Использование Supplier вместо Subtask предпочтительно в общих случаях, когда объект, возвращаемый методом fork, используется только для получения результата подзадачи, завершившейся успешно. Instant deadline = ...
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<String> supplier1 = scope.fork(() -> query(left));
Supplier<String> supplier2 = scope.fork(() -> query(right));
scope.joinUntil(deadline);
scope.throwIfFailed(e -> new WebApplicationException(e));
// both subtasks completed successfully
String result = Stream.of(supplier1, supplier2)
.map(Supplier::get)
.collect(Collectors.joining(", ", "{ ", " }"));
...
}
Расширение StructuredTaskScope
StructuredTaskScope можно расширить, а метод handleComplete переопределить, чтобы реализовать политики, отличные от тех, которые реализуют ShutdownOnSuccess и ShutdownOnFailure. Подкласс может, например, собирать результаты подзадач, которые завершились успешно, и игнорировать подзадачи, которые завершились неудачно. Он может собирать исключения, когда подзадачи завершаются неудачно. Он может вызвать метод shutdown для остановки и заставить join проснуться, когда возникнет какое-либо условие. Подкласс обычно определяет методы для предоставления результатов, состояния или других результатов коду, выполняющемуся после метода join . Подкласс, собирающий результаты и игнорирующий подзадачи, завершившиеся неудачно, может определить метод, возвращающий результаты. Подкласс, реализующий политику остановки при завершении подзадачи неудачно, может определить метод для получения исключения первой подзадачи, завершившейся неудачно.
Ниже приведён пример простой реализации StructuredTaskScope , которая собирает однородные подзадачи, завершившиеся успешно. Он определяет метод "completedSuccessfully()", который главная задача может вызвать после объединения.
class CollectingScope<T> extends StructuredTaskScope<T> {
private final Queue<Subtask<? extends T>> subtasks = new LinkedTransferQueue<>();
@Override
protected void handleComplete(Subtask<? extends T> subtask) {
if (subtask.state() == Subtask.State.SUCCESS) {
subtasks.add(subtask);
}
}
@Override
public CollectingScope<T> join() throws InterruptedException {
super.join();
return this;
}
public Stream<Subtask<? extends T>> completedSuccessfully() {
super.ensureOwnerAndJoined();
return subtasks.stream();
}
}
Реализация метода completedSuccessfully() в примере вызывает ensureOwnerAndJoined() для обеспечения того, что метод может быть вызван только владельческим потоком и только после того, как он объединился.
Деревовидная структура
Области задач образуют дерево, где родительско-дочерние отношения устанавливаются неявно при открытии новой области задачи:- Родительско-дочернее отношение устанавливается, когда поток, запущенный в области задачи, открывает свою собственную область задачи. Поток, запущенный в области задачи "A", открывающий область задачи "B", устанавливает родительско-дочернее отношение, где область задачи "A" является родителем области задачи "B".
- Родительско-дочернее отношение устанавливается при вложенности. Если поток открывает область задачи "B", а затем открывает область задачи "C" (перед закрытием "B"), то содержащая область задачи "B" является родителем вложенной области задачи "C".
Структура дерева поддерживает:
- Наследование связанных значенийПРЕВЬЮ через потоки.
- Проверки ограничения. Фраза "потоки, содержащиеся в области задачи", в описаниях методов, означает потоки, запущенные в области задачи или вложенных областях.
Следующий пример демонстрирует наследование связанного значения. Связанное значение USERNAME привязано к значению "duke". Создаётся StructuredTaskScope и вызывается его метод fork для запуска потока для выполнения
childTask. Поток наследует связанные значения, захваченные при создании области задачи. Код в childTask использует значение связанного значения и, таким образом, читает значение "duke".
private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();
ScopedValue.runWhere(USERNAME, "duke", () -> {
try (var scope = new StructuredTaskScope<String>()) {
scope.fork(() -> childTask());
...
}
});
...
String childTask() {
String name = USERNAME.get(); // "duke"
...
}
StructuredTaskScope в данный момент не определяет API, которые экспонируют структуру дерева.
Если не указано иное, передача аргумента null в конструктор или метод в этом классе приведёт к выбрасыванию NullPointerException.
Влияние на согласованность памяти
Действия в собственном потоке области задачи или потоке, содержащемся в области задачи, до создания подзадачи с помощью fork предшествуют любым действиям, выполненным этой подзадачей, которые, в свою очередь, предшествуют получению результата подзадачи с помощью getПРЕВЬЮ или предшествуют любым действиям, выполненным в потоке после объединения области задачи с помощью join.
- См. Спецификацию языка Java:
- 17.4.5 Порядок предшествования
- С:
- 21
Краткое описание вложенных классов
| Модификатор и тип | Класс | Описание |
|---|---|---|
static final class |
StructuredTaskScope.ShutdownOnFailurePREVIEW |
Предварительный просмотр. Объект, который захватывает исключение первой подзадачи, которая ошибласьПРЕДПРОСМОТР. |
static final class |
StructuredTaskScope.ShutdownOnSuccessPREVIEW<T> |
Предварительный просмотр. Объект, который захватывает результат первой подзадачи, которая завершилась успешноПРЕДПРОСМОТР. |
static interface |
StructuredTaskScope.SubtaskPREVIEW<T> |
Предварительный просмотр. Представляет подзадачу, созданную с помощью fork(Callable). |
Краткое описание конструкторов
| Конструктор | Описание |
|---|---|
StructuredTaskScope() |
Создаёт безымянную структурированную область задач, которая создаёт виртуальные потоки. |
StructuredTaskScope |
Создаёт структурированную область задач с заданным именем и фабрикой потоков. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
void |
close() |
Закрывает эту область задач. |
protected final void |
ensureOwnerAndJoined() |
Обеспечивает, что текущий поток является владельцем этой области задач и что он присоединился (с помощью join() или joinUntil(Instant)) после создания подзадач. |
<U extends T> |
fork |
Запускает новый поток в этой области задач для выполнения задачи, возвращающей значение, создавая тем самым подзадачу этой области задач. |
protected void |
handleComplete |
Вызывается подзадачей при успешном завершении или ошибке в этой области задач. |
final boolean |
isShutdown() |
Возвращает true, если эта область задач закрыта, в противном случае false. |
StructuredTaskScopePREVIEW |
join() |
Ожидает завершения всех подзадач, запущенных в этой области задач, или закрытия области задач. |
StructuredTaskScopePREVIEW |
joinUntil |
Ожидает завершения всех подзадач, запущенных в этой области задач, или закрытия области задач до указанного крайнего срока. |
void |
shutdown() |
Закрывает эту область задач без закрытия. |
Подробное описание конструкторов
StructuredTaskScope
public StructuredTaskScope(String name, ThreadFactory factory)
create потоков при создании дочерних задач. Область задач принадлежит текущему потоку.
Создание сохраняет текущие привязки привязанных значенийПРЕВЬЮ текущего потока для наследования потоками, запущенными в области задач. Раздел Структура дерева в описании класса подробно описывает, как неявно устанавливаются родительско-дочерние отношения для целей наследования привязок привязанных значений.
- Параметры:
-
name- имя области задач, может быть null -
factory- фабрика потоков
StructuredTaskScope
public StructuredTaskScope()
- Требования к реализации:
- Этот конструктор эквивалентен вызову 2-аргументного конструктора с именем
nullи фабрикой потоков, которая создаёт виртуальные потоки.
Подробное описание методов
ensureOwnerAndJoined
protected final void ensureOwnerAndJoined()
join() или joinUntil(Instant)) после создания дочерних задач.- Примечание API:
- Этот метод может использоваться подклассами, которые определяют методы для предоставления результатов, состояния или других результатов коду, предназначенному для выполнения после метода join.
- Исключения:
-
WrongThreadException- если текущий поток не является владельцем области задач -
IllegalStateException- если область задач открыта, и владелец области задач не присоединился после создания
handleComplete
protected void handleComplete(StructuredTaskScope.SubtaskPREVIEW<? extends T> subtask)
- Примечание API:
- Метод
handleCompleteдолжен быть потокобезопасным. Он может вызываться несколькими потоками одновременно. - Требования к реализации:
- Реализация по умолчанию вызывает
NullPointerExceptionесли подзадачаnull. Она вызываетIllegalArgumentException, если подзадача не завершена. - Параметры:
-
subtask- подзадача - Исключения:
-
IllegalArgumentException- если вызывается с подзадачей, которая не завершена
fork
public <U extends T> StructuredTaskScope.SubtaskPREVIEW<U> fork(Callable<? extends U> task)
Задача, возвращающая значение, предоставляется этому методу как Callable, поток выполняет метод call задачи. Поток создается с помощью ThreadFactory области задач. Он наследует текущие привязки привязанных значенийПРЕВЬЮ текущего потока. Привязки должны совпадать с привязками, захваченными при создании области задач.
Этот метод возвращает SubtaskПРЕВЬЮ для представления созданной подзадачи. Объект Subtask может использоваться для получения результата при успешном завершении подзадачи или исключения при её сбое. Для обеспечения корректного использования, методы get()ПРЕВЬЮ и exception()ПРЕВЬЮ могут вызываться только владельцем области задач после ожидания завершения всех потоков с помощью методов join или joinUntil(Instant). При завершении подзадачи, поток вызывает метод handleComplete для обработки завершенной подзадачи. Если область задач закрыта до завершения подзадачи, то метод handleComplete не будет вызван.
Если эта область задач закрыта (или в процессе закрытия), то подзадача не будет запущена, и метод handleComplete не будет вызван.
Этот метод может вызываться только владельцем области задач или потоками, содержащимися в области задач.
- Требования к реализации:
- Этот метод может быть переопределен для целей настройки, например, для обертывания задач. Если переопределен, подкласс должен вызвать
super.forkдля запуска нового потока в этой области задач. - Параметры типа:
-
U- тип результата - Параметры:
-
task- задача, возвращающая значение, для выполнения потоком - Возвращает:
- подзадача
- Исключения:
-
IllegalStateException- если эта область задач закрыта -
WrongThreadException- если текущий поток не является владельцем области задач или потоком, содержащимся в области задач -
StructureViolationExceptionPREVIEW- если текущие привязки scoped value не такие, как при создании области задач -
RejectedExecutionException- если фабрика потоков отклонила создание потока для выполнения подзадачи
join
public StructuredTaskScopePREVIEW<T> join() throws InterruptedException
Этот метод ожидает завершения всех подзадач, ожидая завершения всех потоков запущенных в этой области задач. Он прекращает ожидание, когда все потоки завершаются, область задач закрыта, или текущий поток прерван.
Этот метод может вызываться только владельцем области задач.
- Требования к реализации:
- Этот метод может быть переопределен для целей настройки или для возврата более специфичного типа возвращаемого значения. Если переопределен, подкласс должен вызвать
super.joinдля обеспечения ожидания завершения потоков в этой области задач. - Возвращает:
- эта область задач
- Исключения:
-
IllegalStateException- если эта область задач закрыта -
WrongThreadException- если текущий поток не является владельцем области задач -
InterruptedException- если прервано ожидание
joinUntil
public StructuredTaskScopePREVIEW<T> joinUntil(Instant deadline) throws InterruptedException, TimeoutException
Этот метод ожидает завершения всех подзадач, ожидая завершения всех потоков запущенных в этой области задач. Он прекращает ожидание, когда все потоки завершаются, область задач закрыта, срок истекает, или текущий поток прерван.
Этот метод может вызываться только владельцем области задач.
- Требования к реализации:
- Этот метод может быть переопределен для целей настройки или для возврата более специфичного типа возвращаемого значения. Если переопределен, подкласс должен вызвать
super.joinUntilдля обеспечения ожидания завершения потоков в этой области задач. - Параметры:
-
deadline- срок - Возвращает:
- эта область задач
- Исключения:
-
IllegalStateException- если эта область задач закрыта -
WrongThreadException- если текущий поток не является владельцем области задач -
InterruptedException- если прервано ожидание -
TimeoutException- если срок истек во время ожидания
Остановка
public void shutdown()
join. Остановка полезна в случаях, когда результаты незавершенных подзадач больше не требуются. Как правило, она вызывается реализацией handleComplete(Subtask) подкласса, реализующего политику отбрасывания незавершенных задач после достижения определённого результата. Более конкретно, этот метод:
- Прерывает все незавершенные потоки в области задач (кроме текущего потока).
- Пробуждает владельца области задач, если он ожидает в
join()илиjoinUntil(Instant). Если владелец области задач не ожидает, то его следующий вызовjoinилиjoinUntilвернётся немедленно.
Этот метод может вызываться только владельцем области задач или потоками, содержащимися в области задач.
- Примечание API:
- Возможно, есть потоки, которые не завершены, потому что они выполняют код, который не отреагировал (или не отреагировал оперативно) на прерывание потока. Этот метод не ожидает завершения таких потоков. Когда владелец вызывает метод
closeдля закрытия области задач, он будет ждать завершения оставшихся потоков. - Требования к реализации:
- Этот метод может быть переопределён для настройки. Если он переопределяется, подкласс должен вызвать
super.shutdownдля обеспечения того, что метод остановит область задач. - Исключение:
-
IllegalStateException- если эта область задач закрыта -
WrongThreadException- если текущий поток не является владельцем области задач или потоком, содержащимся в области задач - См. также:
isShutdown
public final boolean isShutdown()
- Возвращает:
- true, если область задач остановлена, иначе false
- См. также:
Закрыть
public void close()
Этот метод сначала останавливает область задач (как если бы он вызывал метод shutdown). Затем он ожидает завершения потоков, выполняющих любые незавершенные задачи. Если происходит прерывание, этот метод будет продолжать ожидать завершения потоков перед завершением с установленным статусом прерывания.
Этот метод может вызываться только владельцем области задач. Если область задач уже закрыта, то вызов этого метода владельцем области задач не оказывает никакого эффекта.
Область задач предназначена для использования структурированным образом. Если этот метод вызывается для закрытия области задач до закрытия вложенных областей задач, то он закрывает базовую конструкцию каждой вложенной области задач (в обратном порядке их создания), закрывает эту область задач и затем генерирует StructureViolationExceptionPREVIEW. Аналогично, если этот метод вызывается для закрытия области задач во время выполнения с привязками значений scoped valuePREVIEW, и область задач была создана до привязки значений, то выбрасывается StructureViolationException после закрытия области задач. Если поток завершается без предварительного закрытия областей задач, которые он владеет, то завершение вызовет закрытие базовой конструкции каждой из его открытых областей задач. Закрытие выполняется в обратном порядке создания областей задач. Поэтому завершение потока может быть отложено, когда владельцу области задач необходимо дождаться завершения потоков, сгенерированных в этих областях задач.
- Определено в:
-
closeв интерфейсеAutoCloseable - Требования к реализации:
- Этот метод может быть переопределён для настройки. Если он переопределяется, подкласс должен вызвать
super.closeдля закрытия области задач. - Исключение:
-
IllegalStateException- генерируется после закрытия области задач, если владелец области задач не попытался присоединиться после создания -
WrongThreadException- если текущий поток не является владельцем области задач -
StructureViolationExceptionPREVIEW- если было обнаружено нарушение структуры
© 1993, 2023, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html
StructuredTaskScopeтолько при включенных предварительных функциях.