Приходилось ли работать с классами синхронизации CountDownLatch, Semaphore, Phaser в Java
Что проверяют
Знаете ли вы готовые примитивы из пакета конкурентности и умеете ли выбрать нужный. Ответ показывает, писали ли вы многопоточный код сами или только пользовались пулом потоков и фреймворком, который всё прятал.
Как отвечать
Пройдитесь по назначению каждого:
CountDownLatchэто одноразовый счётчик. Ожидающие стоят наawait, рабочие потоки уменьшают счётчик черезcountDown, и когда он доходит до нуля, ожидание снимается навсегда. Взвести обратно нельзя, отсюда типичные сценарии: дождаться готовности компонентов при старте, дождаться колбэков в тесте;CyclicBarrierнаоборот переиспользуемый: потоки ждут друг друга в общей точке и идут дальше, барьер можно проходить снова, есть действие на срабатывание;Semaphoreограничивает число одновременных участников. Разрешения берут черезacquireи возвращают черезrelease, есть попытка с таймаутом и честный режим выдачи. Подходит для лимита обращений к внешнему сервису или к дефицитному ресурсу;Phaserэто гибкая комбинация латча и барьера: участников регистрируют и снимают на ходу, а выполнение делится на фазы.
Стоит добавить, что в прикладном коде это чаще нужно в тестах и инфраструктуре, а обычная задача «запустить и дождаться» решается пулом потоков и CompletableFuture.
Где спотыкаются
Ждут без таймаута. Если поток упал до уменьшения счётчика, ожидающий висит вечно и приложение замирает без единой ошибки в логах. Уменьшение счётчика ставят в блок finally, а ожидание ограничивают временем.
Пытаются использовать CountDownLatch повторно. Он не сбрасывается, второй круг ожидания пройдёт мгновенно, и синхронизации просто не будет. Для повторяющихся этапов нужен барьер или Phaser.
Теряют разрешения семафора. Освобождение стоит не в finally, случается исключение, и через некоторое время свободных разрешений не остаётся, а система стоит. Обратная ошибка тоже встречается: освобождают больше, чем брали, и лимит перестаёт что-либо ограничивать, ведь владельца семафор не проверяет.