Класс Phaser
public class Phaser extends Object
CyclicBarrier и CountDownLatch, но допускающий более гибкое использование. Регистрация. В отличие от других барьеров, число зарегистрированных для синхронизации на Phaser участников может меняться со временем. Задачи можно регистрировать в любой момент (с помощью методов register(), bulkRegister(int) или конструкторов, задающих начальное число участников) и при желании отменять их регистрацию при прибытии (с помощью arriveAndDeregister()). Как и в большинстве базовых средств синхронизации, регистрация и отмена регистрации влияют только на внутренние счетчики; они не ведут дополнительного внутреннего учета, поэтому задачи не могут узнать, зарегистрированы ли они. (Однако такой учет можно добавить, создав подкласс этого класса.)
Синхронизация. Как и CyclicBarrier,
Phaser можно ожидать многократно. Метод arriveAndAwaitAdvance() действует аналогично CyclicBarrier.await. Каждому поколению Phaser соответствует номер фазы. Нумерация фаз начинается с нуля и увеличивается, когда все участники прибывают к Phaser, после чего при достижении
Integer.MAX_VALUE цикл начинается снова с нуля. Номера фаз позволяют независимо управлять действиями при прибытии к Phaser и при ожидании остальных с помощью двух видов методов, которые может вызывать любой зарегистрированный участник:
-
Прибытие. Методы
arrive()иarriveAndDeregister()регистрируют прибытие. Эти методы не блокируют выполнение, а возвращают соответствующий номер фазы прибытия, то есть номер фазы Phaser, к которой относится прибытие. Когда прибывает последний участник для данной фазы, выполняется необязательное действие, и фаза увеличивается. Эти действия выполняет участник, вызвавший переход фазы; они определяются переопределением методаonAdvance(int, int), который также управляет завершением. Переопределение этого метода похоже на передачу действия барьера объектуCyclicBarrier, но предоставляет больше гибкости. -
Ожидание. Метод
awaitAdvance(int)принимает аргумент с номером фазы прибытия и возвращает управление, когда Phaser переходит к другой фазе (или уже находится в ней). В отличие от аналогичных конструкций с использованиемCyclicBarrier, методawaitAdvanceпродолжает ожидание, даже если ожидающий поток прерван. Также доступны версии с обработкой прерываний и тайм-аутом, однако исключения, возникающие при прерываемом ожидании задач или ожидании с тайм-аутом, не изменяют состояние Phaser. При необходимости можно выполнить соответствующие действия по восстановлению в обработчиках этих исключений, часто после вызоваforceTermination. Phaser также можно использовать задачам, выполняющимся вForkJoinPool. Прогресс гарантирован, если уровень параллелизма пула позволяет разместить максимальное число одновременно заблокированных участников.
Завершение. Phaser может перейти в состояние завершения, которое можно проверить с помощью метода isTerminated(). После завершения все методы синхронизации немедленно возвращают управление, не дожидаясь перехода фазы, о чем свидетельствует отрицательное возвращаемое значение. Аналогично, попытки регистрации после завершения не дают эффекта. Завершение происходит, когда вызов onAdvance возвращает true. Реализация по умолчанию возвращает
true, если отмена регистрации привела к тому, что число зарегистрированных участников стало равным нулю. Как показано ниже, когда Phaser управляет действиями с фиксированным числом итераций, часто удобно переопределить этот метод, чтобы завершение происходило при достижении текущим номером фазы заданного порога. Для немедленного освобождения ожидающих потоков и возможности их завершения также доступен метод forceTermination().
Иерархическая организация. Phaser можно организовать иерархически (то есть построить в виде дерева), чтобы уменьшить конкуренцию. Вместо Phaser с большим числом участников, который в противном случае подвергался бы значительной конкуренции при синхронизации, можно создать группы подчиненных Phaser с общим родительским Phaser. Это может значительно повысить пропускную способность, несмотря на увеличение накладных расходов на каждую операцию.
В дереве иерархически организованных Phaser регистрация и отмена регистрации дочерних Phaser у родительского выполняются автоматически. Когда число зарегистрированных участников дочернего Phaser становится ненулевым (как задается в конструкторе Phaser(Phaser,int), методах register() или bulkRegister(int)), дочерний Phaser регистрируется у родительского. Когда число зарегистрированных участников становится равным нулю в результате вызова arriveAndDeregister(), дочерний Phaser отменяет регистрацию у родительского.
Мониторинг. Хотя вызывать методы синхронизации могут только зарегистрированные участники, текущее состояние Phaser может отслеживать любой вызывающий код. В любой момент всего имеется getRegisteredParties() участников, из которых getArrivedParties() прибыли в текущей фазе (getPhase()). Когда прибывают остальные (getUnarrivedParties()) участники, фаза увеличивается. Значения, возвращаемые этими методами, могут отражать промежуточные состояния и поэтому обычно не подходят для управления синхронизацией. Метод toString() возвращает снимки этих состояний в форме, удобной для неформального мониторинга.
Эффекты согласованности памяти: действия, выполненные до вызова любого варианта метода прибытия, происходят до соответствующего перехода фазы и действий onAdvance (если они есть), которые, в свою очередь, происходят до действий после перехода фазы.
Примеры использования:
Phaser можно использовать вместо CountDownLatch для управления однократным действием, обслуживающим переменное число участников. Типичный способ — сначала зарегистрироваться в методе настройки, затем запустить все действия и отменить регистрацию, как показано ниже:
void runTasks(List<Runnable> tasks) {
Phaser startingGate = new Phaser(1); // "1" to register self
// create and start threads
for (Runnable task : tasks) {
startingGate.register();
new Thread(() -> {
startingGate.arriveAndAwaitAdvance();
task.run();
}).start();
}
// deregister self to allow threads to proceed
startingGate.arriveAndDeregister();
} Чтобы набор потоков многократно выполнял действия заданное число итераций, можно переопределить onAdvance:
void startTasks(List<Runnable> tasks, int iterations) {
Phaser phaser = new Phaser() {
protected boolean onAdvance(int phase, int registeredParties) {
return phase >= iterations - 1 || registeredParties == 0;
}
};
phaser.register();
for (Runnable task : tasks) {
phaser.register();
new Thread(() -> {
do {
task.run();
phaser.arriveAndAwaitAdvance();
} while (!phaser.isTerminated());
}).start();
}
// allow threads to proceed; don't wait for them
phaser.arriveAndDeregister();
} Если главной задаче позднее потребуется дождаться завершения, она может повторно зарегистрироваться, а затем выполнить аналогичный цикл: // ...
phaser.register();
while (!phaser.isTerminated())
phaser.arriveAndAwaitAdvance(); В ситуациях, когда вы уверены, что номер фазы никогда не перейдет через границу Integer.MAX_VALUE, можно использовать связанные конструкции для ожидания определенных номеров фаз. Например:
void awaitPhase(Phaser phaser, int phase) {
int p = phaser.register(); // assumes caller not already registered
while (p < phase) {
if (phaser.isTerminated())
// ... deal with unexpected termination
else
p = phaser.arriveAndAwaitAdvance();
}
phaser.arriveAndDeregister();
} Чтобы создать набор задач n с помощью дерева Phaser, можно использовать код следующего вида, предполагая, что у класса Task есть конструктор, принимающий Phaser, в котором он регистрируется при создании. После вызова build(new Task[n], 0, n,
new Phaser()) эти задачи можно запустить, например, отправив их в пул:
void build(Task[] tasks, int lo, int hi, Phaser ph) {
if (hi - lo > TASKS_PER_PHASER) {
for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
int j = Math.min(i + TASKS_PER_PHASER, hi);
build(tasks, i, j, new Phaser(ph));
}
} else {
for (int i = lo; i < hi; ++i)
tasks[i] = new Task(ph);
// assumes new Task(ph) performs ph.register()
}
} Наилучшее значение TASKS_PER_PHASER зависит главным образом от ожидаемой частоты синхронизации. Для чрезвычайно коротких тел задач на фазу (и, следовательно, высокой частоты) может подойти значение всего четыре, а для очень объемных тел — несколько сотен. Примечания по реализации: Эта реализация ограничивает максимальное число участников значением 65535. Попытки зарегистрировать дополнительных участников приводят к IllegalStateException. Однако для поддержки сколь угодно больших групп участников можно и следует создавать иерархически организованные Phaser.
- С версии:
- 1.7
Краткое описание конструкторов
| Конструктор | Описание |
|---|---|
Phaser() |
Создает новый Phaser без изначально зарегистрированных участников, без родительского элемента и с начальным номером фазы 0. |
Phaser |
Создает новый Phaser с заданным числом зарегистрированных, но не прибывших участников, без родительского элемента и с начальным номером фазы 0. |
Phaser |
Эквивалентен Phaser(parent, 0). |
Phaser |
Создает новый Phaser с заданными родительским Phaser и числом зарегистрированных, но не прибывших участников. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
int |
arrive() |
Отмечает прибытие к этому Phaser, не дожидаясь прибытия остальных. |
int |
arriveAndAwaitAdvance() |
Отмечает прибытие к этому Phaser и ожидает остальных. |
int |
arriveAndDeregister() |
Отмечает прибытие к этому Phaser и отменяет регистрацию в нем, не дожидаясь прибытия остальных. |
int |
awaitAdvance |
Ожидает перехода фазы этого Phaser от указанного значения; немедленно возвращает управление, если текущая фаза не равна указанному значению или этот Phaser завершен. |
int |
awaitAdvanceInterruptibly |
Ожидает перехода фазы этого Phaser от указанного значения; выбрасывает InterruptedException при прерывании во время ожидания или немедленно возвращает управление, если текущая фаза не равна указанному значению либо этот Phaser завершен. |
int |
awaitAdvanceInterruptibly |
Ожидает перехода фазы этого Phaser от указанного значения или истечения заданного тайм-аута; выбрасывает
InterruptedException при прерывании во время ожидания или немедленно возвращает управление, если текущая фаза не равна указанному значению либо этот Phaser завершен. |
int |
bulkRegister |
Добавляет к этому Phaser указанное число новых не прибывших участников. |
void |
forceTermination() |
Переводит этот Phaser в состояние завершения. |
int |
getArrivedParties() |
Возвращает число зарегистрированных участников, прибывших в текущей фазе этого Phaser. |
Phaser |
getParent() |
Возвращает родительский Phaser этого Phaser или null, если его нет. |
final int |
getPhase() |
Возвращает текущий номер фазы. |
int |
getRegisteredParties() |
Возвращает число участников, зарегистрированных в этом Phaser. |
Phaser |
getRoot() |
Возвращает корневой родительский Phaser для этого Phaser, совпадающий с ним самим, если у него нет родителя. |
int |
getUnarrivedParties() |
Возвращает число зарегистрированных участников, еще не прибывших в текущей фазе этого Phaser. |
boolean |
isTerminated() |
Возвращает true, если этот Phaser завершен. |
protected boolean |
onAdvance |
Метод, который можно переопределить для выполнения действия перед переходом фазы и управления завершением. |
int |
register() |
Добавляет к этому Phaser нового не прибывшего участника. |
String |
toString() |
Возвращает строку, содержащую идентификатор этого Phaser и его состояние. |
Методы, объявленные в классе Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait | Модификатор и тип | Метод | Описание |
|---|---|---|
protected Object |
clone() |
Создает и возвращает копию этого объекта. |
boolean |
equals |
Указывает, равен ли другой объект этому объекту. |
protected void |
finalize() |
Устарел и будет удален: этот элемент API подлежит удалению в одной из будущих версий. Финализация устарела и подлежит удалению в одном из будущих выпусков. |
final Class |
getClass() |
Возвращает класс времени выполнения этого Object. |
int |
hashCode() |
Возвращает хеш-код этого объекта. |
final void |
notify() |
Пробуждает один поток, ожидающий на мониторе этого объекта. |
final void |
notifyAll() |
Пробуждает все потоки, ожидающие на мониторе этого объекта. |
final void |
wait() |
Заставляет текущий поток ожидать пробуждения, обычно в результате уведомления или прерывания. |
final void |
wait |
Заставляет текущий поток ожидать пробуждения, обычно в результате уведомления или прерывания, либо истечения заданного промежутка реального времени. |
final void |
wait |
Заставляет текущий поток ожидать пробуждения, обычно в результате уведомления или прерывания, либо истечения заданного промежутка реального времени. |
Подробное описание конструкторов
Phaser
public Phaser()
Phaser
public Phaser(int parties)
- Параметры:
-
parties— количество участников, необходимое для перехода к следующей фазе - Вызывает исключение:
-
IllegalArgumentException— если количество участников меньше нуля или превышает максимально допустимое количество участников
Phaser
Phaser
public Phaser(Phaser parent, int parties)
- Параметры:
-
parent— родительский фазер -
parties— количество участников, необходимое для перехода к следующей фазе - Вызывает исключение:
-
IllegalArgumentException— если количество участников меньше нуля или превышает максимально допустимое количество участников
Подробное описание методов
register
public int register()
onAdvance(int, int), этот метод может ожидать его завершения, прежде чем вернуться. Если у этого фазера есть родительский фазер и ранее в этом фазере не было зарегистрированных участников, этот дочерний фазер также регистрируется в родительском. Если этот фазер завершен, попытка регистрации не имеет эффекта, и возвращается отрицательное значение.- Возвращает:
- номер фазы прибытия, к которой относится эта регистрация. Если это значение отрицательное, фазер завершен, и регистрация не имеет эффекта.
- Вызывает исключение:
-
IllegalStateException— при попытке зарегистрировать число участников, превышающее максимально допустимое
bulkRegister
public int bulkRegister(int parties)
onAdvance(int, int), этот метод может ожидать его завершения, прежде чем вернуться. Если у этого фазера есть родительский фазер, указанное количество участников больше нуля и ранее в этом фазере не было зарегистрированных участников, этот дочерний фазер также регистрируется в родительском. Если этот фазер завершен, попытка регистрации не имеет эффекта, и возвращается отрицательное значение.- Параметры:
-
parties— количество дополнительных участников, необходимое для перехода к следующей фазе - Возвращает:
- номер фазы прибытия, к которой относится эта регистрация. Если это значение отрицательное, фазер завершен, и регистрация не имеет эффекта.
- Вызывает исключение:
-
IllegalStateException— при попытке зарегистрировать число участников, превышающее максимально допустимое -
IllegalArgumentException— еслиparties < 0
arrive
public int arrive()
Вызов этого метода незарегистрированным участником является ошибкой использования. Однако эта ошибка может привести к
IllegalStateException только при последующей операции с этим фазером, если такая операция вообще будет выполнена.
- Возвращает:
- номер фазы прибытия или отрицательное значение, если фазер завершен
- Вызывает исключение:
-
IllegalStateException— если фазер не завершен и количество не прибывших участников станет отрицательным
arriveAndDeregister
public int arriveAndDeregister()
Вызов этого метода незарегистрированным участником является ошибкой использования. Однако эта ошибка может привести к
IllegalStateException только при последующей операции с этим фазером, если такая операция вообще будет выполнена.
- Возвращает:
- номер фазы прибытия или отрицательное значение, если фазер завершен
- Вызывает исключение:
-
IllegalStateException— если фазер не завершен и количество зарегистрированных или не прибывших участников станет отрицательным
arriveAndAwaitAdvance
public int arriveAndAwaitAdvance()
awaitAdvance(arrive()). Если необходимо ожидание с возможностью прерывания или с тайм-аутом, этого можно добиться с помощью аналогичной конструкции, использующей одну из других форм метода
awaitAdvance. Если при прибытии необходимо отменить регистрацию, используйте awaitAdvance(arriveAndDeregister()). Вызов этого метода незарегистрированным участником является ошибкой использования. Однако эта ошибка может привести к
IllegalStateException только при последующей операции с этим фазером, если такая операция вообще будет выполнена.
- Возвращает:
- номер фазы прибытия или (отрицательную) текущую фазу, если фазер завершен
- Вызывает исключение:
-
IllegalStateException— если фазер не завершен и количество не прибывших участников станет отрицательным
awaitAdvance
public int awaitAdvance(int phase)
- Параметры:
-
phase— номер фазы прибытия или отрицательное значение, если фазер завершен; обычно этот аргумент является значением, возвращенным предыдущим вызовомarriveилиarriveAndDeregister. - Возвращает:
- номер следующей фазы прибытия, аргумент, если он отрицательный, или (отрицательную) текущую фазу, если фазер завершен
awaitAdvanceInterruptibly
public int awaitAdvanceInterruptibly(int phase) throws InterruptedException
InterruptedException. Немедленно возвращает управление, если текущая фаза не равна указанному значению или этот фазер завершен.- Параметры:
-
phase— номер фазы прибытия или отрицательное значение, если фазер завершен; обычно этот аргумент является значением, возвращенным предыдущим вызовомarriveилиarriveAndDeregister. - Возвращает:
- номер следующей фазы прибытия, аргумент, если он отрицательный, или (отрицательную) текущую фазу, если фазер завершен
- Вызывает исключение:
-
InterruptedException— если поток прерван во время ожидания
awaitAdvanceInterruptibly
public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
InterruptedException. Немедленно возвращает управление, если текущая фаза не равна указанному значению или этот фазер завершен.- Параметры:
-
phase— номер фазы прибытия или отрицательное значение, если фазер завершен; обычно этот аргумент является значением, возвращенным предыдущим вызовомarriveилиarriveAndDeregister. -
timeout— сколько ждать до прекращения ожидания в единицахunit -
unit—TimeUnit, определяющий интерпретацию параметраtimeout - Возвращает:
- номер следующей фазы прибытия, аргумент, если он отрицательный, или (отрицательную) текущую фазу, если фазер завершен
- Вызывает исключение:
-
InterruptedException— если поток прерван во время ожидания -
TimeoutException— если время ожидания истекло
forceTermination
public void forceTermination()
getPhase
public final int getPhase()
Integer.MAX_VALUE, после которого отсчет начинается с нуля. После завершения фазера номер фазы становится отрицательным; в этом случае номер фазы, действовавший непосредственно перед завершением, можно получить с помощью getPhase() + Integer.MIN_VALUE.- Возвращает:
- номер фазы или отрицательное значение, если фазер завершен
getRegisteredParties
public int getRegisteredParties()
- Возвращает:
- количество участников
getArrivedParties
public int getArrivedParties()
- Возвращает:
- количество прибывших участников
getUnarrivedParties
public int getUnarrivedParties()
- Возвращает:
- количество не прибывших участников
getParent
public Phaser getParent()
null, если его нет.- Возвращает:
- родительский фазер этого фазера или
null, если его нет
getRoot
public Phaser getRoot()
- Возвращает:
- корневой родительский фазер этого фазера
isTerminated
public boolean isTerminated()
true, если этот фазер завершен.- Возвращает:
-
true, если этот фазер завершен
onAdvance
protected boolean onAdvance(int phase, int registeredParties)
true, при переходе этот фазер будет переведен в окончательное состояние завершения, а последующие вызовы isTerminated() вернут true. Любое непроверяемое исключение или ошибка, выброшенные при вызове этого метода, передаются участнику, пытающемуся продвинуть этот фазер; в таком случае переход не выполняется. Аргументы этого метода задают состояние фазера, действующее для текущего перехода. Поведение при вызове изнутри onAdvance методов прибытия, регистрации и ожидания для этого фазера не определено, и полагаться на него не следует.
Если этот фазер является частью многоуровневого набора фазеров, то onAdvance вызывается при каждом переходе только для корневого фазера.
Для поддержки наиболее распространенных сценариев использования реализация этого метода по умолчанию возвращает true, когда количество зарегистрированных участников становится равным нулю в результате вызова участником arriveAndDeregister. Это поведение можно отключить и тем самым разрешить продолжение работы после последующих регистраций, переопределив этот метод так, чтобы он всегда возвращал false:
Phaser phaser = new Phaser() {
protected boolean onAdvance(int phase, int parties) { return false; }
};
- Параметры:
-
phase— номер текущей фазы при входе в этот метод, до перехода этого фазера -
registeredParties— текущее количество зарегистрированных участников - Возвращает:
-
true, если этот фазер должен завершиться
toString
public String toString()
"phase = ", за которой следует номер фазы, "parties = ", за которым следует количество зарегистрированных участников, и
"arrived = ", за которым следует количество прибывших участников.
© 1993, 2025, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.