Класс Phaser
public class Phaser extends Object
CyclicBarrier и CountDownLatch, но с более гибким использованием. Регистрация. В отличие от других барьеров, количество участников, зарегистрированных для синхронизации на фазер, может меняться со временем. Задачи могут регистрироваться в любое время (с помощью методов register(), bulkRegister(int) или форм конструкторов, устанавливающих начальное количество участников), и при необходимости дерегистрироваться при любом прибытии (с помощью arriveAndDeregister()). Как и в большинстве основных синхронизационных конструкций, регистрация и дерегистрация влияют только на внутренние счетчики; они не устанавливают дальнейшее внутреннее ведение записей, поэтому задачи не могут запросить, зарегистрированы ли они. (Однако вы можете ввести такое ведение записей, унаследовав этот класс.)
Синхронизация. Как и CyclicBarrier, фазер может многократно ожидаться. Метод 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 задач с использованием дерева фазеров можно использовать код следующего вида, предполагая класс задачи с конструктором, принимающим 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. Однако вы можете и должны создавать иерархические фазеры, чтобы вместить произвольно большие наборы участников.
- С:
- 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
public Phaser(Phaser parent)
Phaser(parent, 0).- Параметры:
-
parent- родительский фазер
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 = " за которым следует количество прибывших сторон.- Переопределяет:
-
toStringв классеObject - Возвращает:
- строку, идентифицирующую этот фазер, а также его состояние
© 1993, 2021, 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://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Phaser.html