Gymterview
senior

Что такое Phaser и когда его использовать?

Phaser — это синхронизатор из java.util.concurrent (Java 7), который объединяет функциональность CountDownLatch и CyclicBarrier и дополняет их динамическим управлением количеством участников и поддержкой нескольких фаз.

Основные концепции:

  • Фаза (phase) — текущий этап синхронизации. Номер начинается с 0.
  • Участник (party) — зарегистрированный поток. Количество можно изменять динамически.
  • Прибытие (arrive) — сигнал от участника о завершении текущей фазы.
Код: три потока, три фазы обработки
Phaser phaser = new Phaser(3); // 3 участника

for (int i = 0; i < 3; i++) {
    final int threadId = i;
    new Thread(() -> {
        // Фаза 0: Загрузка данных
        System.out.println("Поток " + threadId + ": загрузка данных");
        phaser.arriveAndAwaitAdvance(); // Ждём, пока все загрузят данные

        // Фаза 1: Обработка данных
        System.out.println("Поток " + threadId + ": обработка данных");
        phaser.arriveAndAwaitAdvance();

        // Фаза 2: Сохранение результатов
        System.out.println("Поток " + threadId + ": сохранение результатов");
        phaser.arriveAndDeregister(); // Завершаем участие
    }).start();
}

Динамическое управление участниками:

Пример
Phaser phaser = new Phaser(1); // Главный поток — 1 участник

for (int i = 0; i < 5; i++) {
    phaser.register(); // Регистрируем нового участника
    new Thread(() -> {
        try {
            doWork();
        } finally {
            phaser.arriveAndDeregister();
        }
    }).start();
}

phaser.arriveAndAwaitAdvance(); // Главный поток ждёт завершения всех

Переопределение onAdvance для управления фазами:

Пример
Phaser phaser = new Phaser(3) {
    @Override
    protected boolean onAdvance(int phase, int registeredParties) {
        System.out.println("Фаза " + phase + " завершена. Участников: " + registeredParties);
        return phase >= 2 || registeredParties == 0; // Завершить после 3 фаз
    }
};

Сравнение синхронизаторов:

Характеристика CountDownLatch CyclicBarrier Phaser
Повторное использование Нет Да Да
Динамическое число участников Нет Нет Да
Несколько фаз Нет Да (одинаковые) Да (разные)
Иерархическая структура Нет Нет Да (tiered phaser)

Основные методы:

  • arriveAndAwaitAdvance() — «я прибыл и жду остальных».
  • arrive() — «я прибыл, но не жду» (неблокирующий).
  • arriveAndDeregister() — «я прибыл и выхожу из игры».
  • register() — добавить нового участника.

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

  • Забытая дерегистрация — остальные потоки зависнут.
  • Использование для простых сценариев — CountDownLatch проще.
  • Начальное число участников 0 — Phaser считается завершённым.

Аналогия: Phaser — это многодневный поход. Группа (участники) проходит маршрут поэтапно (фазы). На каждом привале (барьер) все ждут друг друга. Кто-то может присоединиться по дороге (register), кто-то — сойти (deregister). Руководитель (onAdvance) решает, продолжать ли путь.

На собеседовании ключевой вопрос: «Когда Phaser лучше CyclicBarrier?» Ответ: когда число участников меняется динамически или нужна иерархическая структура (tiered phasers) для масштабирования.