MXStat
MXStat
Введите текст для поиска
Расширенный поиск каналов
  • Вход на сайт
  • Каталог
    Каталог каналов Региональные подборки Поиск каналов
    Добавить канал
  • Рейтинги
    Рейтинг каналов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Max
Java Geek

21 Aug, 10:54

Открыть в Max Поделиться

🏗 Java: Структурная конкурентность. Прощайте, зомби-потоки!

Допустим, вы используете Виртуальные потоки (Project Loom), чтобы сделать два независимых запроса: получить данные пользователя (API 1) и его заказы (API 2).

Раньше мы использовали ExecutorService или CompletableFuture. Но у них есть огромная архитектурная дыра: они ничего не знают друг о друге.

🧟‍♂️ Проблема зомби-потоков (Unstructured Concurrency)

Если API 1 мгновенно падает с ошибкой 500, ваш метод все равно будет ждать, пока API 2 доработает (или упадет по таймауту). Поток, качающий заказы, становится "сиротой". Он делает бесполезную работу, тратит сеть и память, хотя результат уже никому не нужен.

А если ошибку выкинет родительский метод? Дочерние потоки продолжат жить своей жизнью в фоне. Это хаос.

🧩 Решение: StructuredTaskScope

В современной Java (начиная с 21 версии) потоки привязали к лексической области видимости - блоку кода. Если мы выходим из блока (из-за ошибки или успешного завершения), все запущенные внутри него дочерние потоки автоматически отменяются (получают interrupt).

Вот как выглядит "Запрос-Ответ", где должны выполниться оба действия (Стратегия *All or Nothing*):

// ShutdownOnFailure: если один упал, отменяем остальные
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

// Запускаем подзадачи (Subtasks)
Subtask<User> user = scope.fork(() -> fetchUser(id));
Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(id));

scope.join(); // Ждем завершения обеих задач...
scope.throwIfFailed(); // ...или первой же ошибки!

// Сюда дойдем, только если обе задачи успешны
return new UserProfile(user.get(), orders.get());
}
// При выходе из блока любые зависшие потоки будут убиты

🏎 Стратегия "Кто первый, тот и прав"

А что, если вам нужно получить курс валют, и у вас есть 3 разных провайдера? Вам нужен ответ от любого, кто ответит быстрее.

// ShutdownOnSuccess: первый успешный отменяет остальные
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Quote>()) {

scope.fork(() -> getFromBankA());
scope.fork(() -> getFromBankB());
scope.fork(() -> getFromBankC());

scope.join(); // Ждем первого успешного

return scope.result(); // Возвращаем самый быстрый ответ
}

Как только Банк А ответит, запросы к Банкам B и C будут немедленно отменены. Никакого ручного управления Future.cancel(). Всё работает из коробки.

🧠 Почему это меняет всё?

1. Читаемость: Многопоточный код читается сверху вниз, как обычный синхронный.
2. Безопасность ресурсов: Утечки потоков физически невозможны. Структура гарантирует, что родитель не завершится, пока не разберется со всеми детьми.
3. Идеальные логи: Стек-трейс теперь показывает реальную иерархию (кто кого вызвал), а не обрывается на внутренностях пула потоков.

В связке с Виртуальными потоками это делает Java одним из самых удобных языков для написания высоконагруженных сетевых приложений.

#Concurrency #ProjectLoom #CleanCode #Backend

97 1
Каталог
Каталог каналов Подборки каналов Поиск каналов Добавить канал
Рейтинги
Рейтинг каналов Max
Контакты
Написать в Max Написать в Telegram Написать на почту
Всякая всячина
Пользовательское соглашение Политика конфиденциальности
Наши каналы
MXStat в Telegram MXStat в Max
Наши боты
MXAuthBot MXAnalyticsBot
Made by TGStat