Gymterview
junior

Structured Concurrency — что это и зачем?

Structured Concurrency — API для управления группами связанных задач как единым целым. Гарантирует, что подзадачи завершатся (или будут отменены) до завершения родительской задачи, предотвращая утечки потоков.

Аналогия из жизни: как руководитель проекта, который не уходит домой, пока все члены команды не сдали свои части работы. Если один провалил задачу — остальным сообщают, что проект отменён, и они тоже прекращают работу.

Пример: ShutdownOnFailure и ShutdownOnSuccess
// Проблема: без Structured Concurrency
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
Future<User> userFuture = executor.submit(() -> fetchUser(id));
Future<List<Order>> ordersFuture = executor.submit(() -> fetchOrders(id));
// Если fetchOrders бросит исключение — fetchUser продолжает выполняться!
// Если основной поток прервётся — подзадачи «утекают»

// Решение: Structured Concurrency
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Subtask<User> user = scope.fork(() -> fetchUser(id));
    Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(id));

    scope.join();           // ждём завершения обеих задач
    scope.throwIfFailed();  // бросить исключение, если одна из задач упала

    return new UserWithOrders(user.get(), orders.get());
}
// При ошибке одной задачи — другая отменяется автоматически

// ShutdownOnSuccess — вернуть первый успешный результат
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
    scope.fork(() -> fetchFromPrimary());
    scope.fork(() -> fetchFromFallback());
    scope.join();
    return scope.result(); // первый успешный
}

Стратегии

Стратегия Поведение Когда использовать
ShutdownOnFailure Отменить все при первой ошибке Параллельные зависимые запросы
ShutdownOnSuccess Вернуть первый успешный результат Racing, fallback

Частые ошибки

  • Не вызывать join() — без join() результаты недоступны и подзадачи могут утечь
  • Использовать вне try-with-resources — scope должен быть закрыт для гарантии завершения
  • Путать с CompletableFuture.allOf() — Structured Concurrency проще и безопаснее для fork-join паттерна

Как используется в 2026

  • Structured Concurrency финализирован и активно используется с Virtual Threads
  • Заменяет ручное управление Future + ExecutorService для параллельных запросов
  • В Spring-приложениях — для параллельного вызова нескольких микросервисов

На собеседовании: объясните проблему (утечка подзадач при ошибке или отмене), решение (жизненный цикл подзадач привязан к scope), и две стратегии. Частая ошибка — не понимать, зачем нужен try-with-resources для scope.