Класс Phaser
public class Phaser extends Object
CyclicBarrier и CountDownLatch, но поддерживающий более гибкое использование. Регистрация. В отличие от других барьеров, количество сторон, зарегистрированных для синхронизации на фазере, может изменяться со временем. Задачи могут быть зарегистрированы в любое время (с помощью методов register(), bulkRegister(int) или форм конструкторов, устанавливающих начальное количество сторон), и необязательно дезактивированы при любом прибытии (с помощью arriveAndDeregister()). Как и в большинстве основных конструкций синхронизации, регистрация и дерегистрация влияют только на внутренние счётчики; они не устанавливают дополнительную внутреннюю учётную запись, поэтому задачи не могут запросить, зарегистрированы ли они. (Однако вы можете ввести такую учётную запись, унаследовав этот класс.)
Синхронизация. Как и CyclicBarrier, за
Phaser можно неоднократно ожидать. Метод arriveAndAwaitAdvance() имеет эффект, аналогичный CyclicBarrier.await. Каждая генерация фазера имеет связанный номер фазы. Номер фазы начинается с нуля и увеличивается, когда все стороны прибывают в фазер, возвращаясь к нулю после достижения
Integer.MAX_VALUE. Использование номеров фаз позволяет независимо контролировать действия при прибытии в фазер и при ожиданиях других, с помощью двух видов методов, которые могут быть вызваны любой зарегистрированной стороной:
-
Прибытие. Методы
arrive()иarriveAndDeregister()регистрируют прибытие. Эти методы не блокируют, а возвращают связанный номер фазы прибытия; то есть номер фазы фазера, к которому было применено прибытие. Когда прибывает последняя сторона для данной фазы, выполняется необязательное действие, и фаза переходит на следующую. Эти действия выполняются стороной, вызывающей переход фазы, и организуются путём переопределения методаonAdvance(int, int), который также управляет завершением. Переопределение этого метода похоже на, но более гибкое, чем предоставление действия барьера дляCyclicBarrier. -
Ожидание. Метод
awaitAdvance(int)требует аргумента, указывающего номер фазы прибытия, и возвращает значение, когда фазер переходит на (или уже находится на) другую фазу. В отличие от похожих конструкций с использованиемCyclicBarrier, методawaitAdvanceпродолжает ждать, даже если поток ожидания прерван. Также доступны прерывимые и временные версии, но исключения, возникшие во время ожидания задач прерывисто или с тайм-аутом, не изменяют состояние фазера. При необходимости вы можете выполнить связанное восстановление внутри обработчиков этих исключений, часто после вызоваforceTermination. Фазеры также могут использоваться задачами, выполняющимися вForkJoinPool. Прогресс гарантируется, если уровень параллелизма пула может вместить максимальное количество одновременных заблокированных сторон.
Завершение. Фазер может перейти в состояние завершения, которое можно проверить с помощью метода isTerminated(). При завершении все методы синхронизации немедленно возвращают значение без ожидания перехода, как указано отрицательным возвращаемым значением. Аналогично, попытки зарегистрироваться при завершении не имеют эффекта. Завершение инициируется, когда вызов onAdvance возвращает true. В реализации по умолчанию возвращается
true, если дерегистрация привела к тому, что количество зарегистрированных сторон стало равным нулю. Как показано ниже, когда фазеры контролируют действия с фиксированным количеством итераций, часто удобно переопределить этот метод для того, чтобы вызвать завершение, когда текущий номер фазы достигнет порога. Также доступен метод forceTermination() для внезапного высвобождения ожидающих потоков и разрешения их завершения.
Иерархия. Фазеры могут быть иерархическими (то есть построены в структуре дерева) для уменьшения конфликтов. Фазеры с большим количеством сторон, которые в противном случае столкнулись бы с большими затратами на конфликты синхронизации, могут быть настроены так, чтобы группы подфазеров разделяли общего родителя. Это может значительно увеличить пропускную способность, хотя это влечёт большие накладные расходы на каждую операцию.
В дереве иерархических фазеров регистрация и дерегистрация дочерних фазеров у их родителя управляются автоматически. Всякий раз, когда количество зарегистрированных сторон дочернего фазера становится ненулевым (как установлено в конструкторе Phaser(Phaser,int), register() или bulkRegister(int)), дочерний фазер регистрируется у своего родителя. Всякий раз, когда количество зарегистрированных сторон становится нулевым в результате вызова arriveAndDeregister(), дочерний фазер дерегистрируется у своего родителя.
Мониторинг. Хотя методы синхронизации могут вызываться только зарегистрированными сторонами, текущее состояние фазера может быть отслеживаемо любым вызывающим объектом. В любой момент времени есть getRegisteredParties() сторон в общей сложности, из которых getArrivedParties() прибыли на текущую фазу (getPhase()). Когда остальные (getUnarrivedParties()) стороны прибудут, фаза переходит на следующую. Возвращаемые значения этих методов могут отражать временные состояния, поэтому они, как правило, не пригодны для управления синхронизацией. Метод toString() возвращает снимки этих запросов состояния в удобной для неформального мониторинга форме.
Эффекты согласованности памяти: Действия, предшествующие любому виду метода arrive, предшествуют соответствующему переходу фазы и действиям onAdvance (если они присутствуют), которые, в свою очередь, предшествуют действиям, следующим за переходом фазы.
Примеры использования:
Фазер может быть использован вместо 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 задач с помощью дерева фазеров вы можете использовать код следующей формы, предполагая класс 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. Однако вы можете и должны создавать иерархические фазеры для размещения произвольно больших наборов участников.
- Since:
- 1.7
Краткое описание конструкторов
| Конструктор | Описание |
|---|---|
Phaser() |
Создаёт новый фазер без первоначально зарегистрированных участников, без родительского фазера и начальным номером фазы 0. |
Phaser |
Создаёт новый фазер с заданным количеством зарегистрированных не пришедших участников, без родительского фазера и начальным номером фазы 0. |
Phaser |
Эквивалентно Phaser(parent, 0). |
Phaser |
Создаёт новый фазер с заданным родительским фазером и количеством зарегистрированных не пришедших участников. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
int |
arrive() |
Приходит в этот фазер без ожидания прихода других. |
int |
arriveAndAwaitAdvance() |
Приходит в этот фазер и ожидает других. |
int |
arriveAndDeregister() |
Приходит в этот фазер и отписывается от него без ожидания прихода других. |
int |
awaitAdvance |
Ожидает продвижения фазы этого фазера от заданного значения фазы, возвращаясь немедленно, если текущая фаза не равна заданному значению фазы или этот фазер завершён. |
int |
awaitAdvanceInterruptibly |
Ожидает продвижения фазы этого фазера от заданного значения фазы, выбрасывая InterruptedException, если прерван во время ожидания, или возвращаясь немедленно, если текущая фаза не равна заданному значению фазы или этот фазер завершён. |
int |
awaitAdvanceInterruptibly |
Ожидает продвижения фазы этого фазера от заданного значения фазы или истечения заданного таймаута, выбрасывая
InterruptedException, если прерван во время ожидания, или возвращаясь немедленно, если текущая фаза не равна заданному значению фазы или этот фазер завершён. |
int |
bulkRegister |
Добавляет заданное количество новых не пришедших участников в этот фазер. |
void |
forceTermination() |
Принудительно переводит этот фазер в состояние завершения. |
int |
getArrivedParties() |
Возвращает количество зарегистрированных участников, которые пришли в текущей фазе этого фазера. |
Phaser |
getParent() |
Возвращает родительского фазера этого фазера, или null, если нет. |
final int |
getPhase() |
Возвращает текущий номер фазы. |
int |
getRegisteredParties() |
Возвращает количество участников, зарегистрированных в этом фазере. |
Phaser |
getRoot() |
Возвращает корневого предка этого фазера, который такой же, как и этот фазер, если у него нет родительского фазера. |
int |
getUnarrivedParties() |
Возвращает количество зарегистрированных участников, которые ещё не пришли в текущей фазе этого фазера. |
boolean |
isTerminated() |
Возвращает true, если этот фазер завершён. |
protected boolean |
onAdvance |
Переопределяемый метод для выполнения действия при приближающемся продвижении фазы и управления завершением. |
int |
register() |
Добавляет нового не пришедшего участника в этот фазер. |
String |
toString() |
Возвращает строку, идентифицирующую этот фазер, а также его состояние. |
Подробное описание конструкторов
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.
https://download.java.net/java/early_access/jdk24/docs/api/java.base/java/util/concurrent/Phaser.html