Что такое 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) для масштабирования.