Интерфейс Condition
- Все известные реализующие классы:
-
AbstractQueuedLongSynchronizer.ConditionObject,AbstractQueuedSynchronizer.ConditionObject
public interface Condition
Condition выделяет методы мониторинга Object (wait, notify и notifyAll) в отдельные объекты, чтобы имитировать наличие нескольких наборов ожидания на объект, объединяя их с использованием произвольных реализаций Lock. В тех случаях, где Lock заменяет использование методов и операторов synchronized, Condition заменяет использование методов мониторинга объекта. Условные переменные (также известные как очереди условного ожидания или условные переменные) предоставляют способ приостановки выполнения одного потока ("ожидание"), пока другой поток не уведомит о том, что некоторое состояние может быть истинным. Поскольку доступ к этой общей информации о состоянии происходит в разных потоках, это необходимо защитить, поэтому условная переменная связана с каким-либо замком. Ключевым свойством ожидания условной переменной является то, что она атомарно освобождает связанный замок и приостанавливает текущий поток, точно так же, как Object.wait.
Экземпляр Condition неразрывно связан с замком. Для получения экземпляра Condition для конкретного экземпляра Lock используйте его метод newCondition().
Например, предположим, что у нас есть буфер ограниченной длины, который поддерживает методы put и take. Если попытка take выполняется для пустого буфера, то поток блокируется, пока не появится элемент; если попытка put выполняется для полного буфера, то поток блокируется, пока не появится место. Мы хотим сохранить ожидание put потоков и take потоков в отдельных наборах ожидания, чтобы мы могли использовать оптимизацию, уведомляя только один поток за раз, когда в буфере становятся доступными элементы или места. Это можно сделать, используя два экземпляра Condition.
class BoundedBuffer<E> {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(E x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
E x = (E) items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
(Класс ArrayBlockingQueue предоставляет эту функциональность, поэтому нет необходимости реализовывать этот пример класса.) Реализация Condition может обеспечить поведение и семантику, отличную от методов мониторинга Object, например, гарантированную последовательность уведомлений или отсутствие необходимости удерживать замок при выполнении уведомлений. Если реализация предоставляет такую специализированную семантику, то эта реализация должна задокументировать эту семантику.
Обратите внимание, что экземпляры Condition — это обычные объекты, и их сами можно использовать в качестве целевого объекта в инструкции synchronized, а также можно вызывать их собственные методы мониторинга wait и notify. Получение блокировки монитора экземпляра Condition или использование его методов монитора не имеет указанных отношений с получением Lock, связанного с этим экземпляром Condition или использованием его методов ожидания и сигнализации. Рекомендуется избегать использования экземпляров Condition таким образом, за исключением, возможно, их собственной реализации.
Если не указано иное, передача значения null для любого параметра приведет к выбрасыванию NullPointerException.
Рекомендации по реализации
При ожидании условной переменной, в общем случае, допускается «ложное пробуждение», что обусловлено семантикой базовой платформы. В большинстве приложений это мало влияет, так как условная переменная всегда должна ожидаться в цикле, проверяя предикат состояния, для которого происходит ожидание. Реализация может устранить возможность ложных пробуждений, но рекомендуется, чтобы разработчики приложений всегда предполагали, что они могут произойти, и поэтому всегда ждут в цикле.
Три формы ожидания условных переменных (прерывистое, непрерывистое и с временем ожидания) могут отличаться по сложности реализации на некоторых платформах и по характеристикам производительности. В частности, может быть сложно предоставить эти функции и сохранить определенную семантику, например, гарантии порядка. Кроме того, возможность прервать фактическую приостановку потока может не всегда быть реализуема на всех платформах.
Поэтому реализация не обязана определять точно те же гарантии или семантику для всех трех форм ожидания, а также не обязана поддерживать прерывание фактической приостановки потока.
Реализация должна четко задокументировать семантику и гарантии, предоставляемые каждым из методов ожидания, а при поддержке реализацией прерывания приостановки потока она должна соответствовать семантике прерывания, как определено в этом интерфейсе.
Поскольку прерывание, как правило, подразумевает отмену, а проверки на прерывание часто редки, реализация может отдавать предпочтение реагированию на прерывание перед обычным возвратом из метода. Это справедливо даже в том случае, если можно показать, что прерывание произошло после другого действия, которое могло разблокировать поток. Реализация должна задокументировать это поведение.
- С:
- 1.5
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
void |
await() |
Принуждает текущий поток ожидать, пока он не получит сигнал или прервется. |
boolean |
await |
Принуждает текущий поток ожидать, пока он не получит сигнал или не прервется, или не истечет указанное время ожидания. |
long |
awaitNanos |
Принуждает текущий поток ожидать, пока он не получит сигнал или не прервется, или не истечет указанное время ожидания. |
void |
awaitUninterruptibly() |
Принуждает текущий поток ожидать, пока он не получит сигнал. |
boolean |
awaitUntil |
Принуждает текущий поток ожидать, пока он не получит сигнал или не прервется, или не истечет указанное время. |
void |
signal() |
Разбудит один ожидающий поток. |
void |
signalAll() |
Разбудит все ожидающие потоки. |
Подробное описание методов
await
void await() throws InterruptedException
Блокировка, связанная с этим Condition, атомарно освобождается, и текущий поток становится неактивным для планирования потоков и находится в спящем режиме, пока не произойдёт одно из четырёх событий:
- Другой поток вызывает метод
signal()для этогоConditionи текущий поток выбирается в качестве потока, который нужно разбудить; или - Другой поток вызывает метод
signalAll()для этогоCondition; или - Другой поток прерывает текущий поток, и поддержка прерывания приостановки потока включена; или
- Происходит «ложное пробуждение».
Во всех случаях, прежде чем этот метод может вернуть значение, текущий поток должен повторно получить блокировку, связанную с этим условием. При возврате потока он гарантированно удерживает эту блокировку.
Если в текущем потоке:
- флаг прерывания установлен при входе в этот метод; или
- происходит прерывание во время ожидания, и поддержка прерывания приостановки потока включена,
InterruptedException, а флаг прерывания текущего потока сбрасывается. В первом случае не указано, происходит ли проверка прерывания до или после освобождения блокировки. Рекомендации по реализации
Предполагается, что текущий поток удерживает блокировку, связанную с этим Condition, когда вызывается этот метод. Реализация должна определить, так ли это, и, если нет, как на это реагировать. Обычно будет выбрасываться исключение (такое как IllegalMonitorStateException), и реализация должна это документировать.
Реализация может отдавать приоритет реагированию на прерывание перед обычным возвратом метода в ответ на сигнал. В этом случае реализация должна гарантировать, что сигнал перенаправляется другому ожидающему потоку, если такой есть.
- Исключения:
-
InterruptedException- если текущий поток прерван (и поддерживается прерывание приостановки потока)
awaitUninterruptibly
void awaitUninterruptibly()
Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток становится неактивным для планирования потоков и находится в спящем режиме, пока не произойдёт одно из трёх событий:
- Другой поток вызывает метод
signal()для этогоConditionи текущий поток выбирается в качестве потока, который нужно разбудить; или - Другой поток вызывает метод
signalAll()для этогоCondition; или - Происходит «ложное пробуждение».
Во всех случаях, прежде чем этот метод может вернуть значение, текущий поток должен повторно получить блокировку, связанную с этим условием. При возврате потока он гарантированно удерживает эту блокировку.
Если флаг прерывания текущего потока установлен при входе в этот метод или он прерывается во время ожидания, он продолжит ожидание, пока не будет сигнализирован. Когда он, наконец, вернётся из этого метода, его флаг прерывания по-прежнему будет установлен.
Рекомендации по реализации
Предполагается, что текущий поток удерживает блокировку, связанную с этим Condition, когда вызывается этот метод. Реализация должна определить, так ли это, и, если нет, как на это реагировать. Обычно будет выбрасываться исключение (такое как IllegalMonitorStateException), и реализация должна это документировать.
awaitNanos
long awaitNanos(long nanosTimeout) throws InterruptedException
Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток становится неактивным для планирования потоков и находится в спящем режиме, пока не произойдёт одно из пяти событий:
- Другой поток вызывает метод
signal()для этогоConditionи текущий поток выбирается в качестве потока, который нужно разбудить; или - Другой поток вызывает метод
signalAll()для этогоCondition; или - Другой поток прерывает текущий поток, и поддержка прерывания приостановки потока включена; или
- Истекает заданное время ожидания; или
- Происходит «ложное пробуждение».
Во всех случаях, прежде чем этот метод может вернуть значение, текущий поток должен повторно получить блокировку, связанную с этим условием. При возврате потока он гарантированно удерживает эту блокировку.
Если в текущем потоке:
- флаг прерывания установлен при входе в этот метод; или
- происходит прерывание во время ожидания, и поддержка прерывания приостановки потока включена,
InterruptedException, а флаг прерывания текущего потока сбрасывается. В первом случае не указано, происходит ли проверка прерывания до или после освобождения блокировки. Метод возвращает оценку количества оставшихся наносекунд ожидания, учитывая предоставленное значение nanosTimeout при возврате, или значение меньше или равно нулю, если ожидание истекло. Это значение может использоваться для определения, следует ли и как долго повторно ожидать в тех случаях, когда метод возвращает значение, но ожидаемое условие по-прежнему не выполняется. Типичные использования этого метода выглядят следующим образом:
boolean aMethod(long timeout, TimeUnit unit)
throws InterruptedException {
long nanosRemaining = unit.toNanos(timeout);
lock.lock();
try {
while (!conditionBeingWaitedFor()) {
if (nanosRemaining <= 0L)
return false;
nanosRemaining = theCondition.awaitNanos(nanosRemaining);
}
// ...
return true;
} finally {
lock.unlock();
}
} Примечание к проектированию: Этот метод требует аргумента в наносекундах, чтобы избежать ошибок усечения при отслеживании оставшегося времени. Такая потеря точности затруднила бы программистам гарантию того, что общее время ожидания не будет систематически меньше указанного при повторных ожиданиях.
Рекомендации по реализации
Предполагается, что текущий поток удерживает блокировку, связанную с этим Condition, когда вызывается этот метод. Реализация должна определить, так ли это, и, если нет, как на это реагировать. Обычно будет выбрасываться исключение (такое как IllegalMonitorStateException), и реализация должна это документировать.
Реализация может отдавать приоритет реагированию на прерывание перед обычным возвратом метода в ответ на сигнал или перед указанием истечения заданного времени ожидания. В любом случае реализация должна гарантировать, что сигнал перенаправляется другому ожидающему потоку, если такой есть.
- Параметры:
-
nanosTimeout- максимальное время ожидания в наносекундах - Возвращает:
- оценка значения
nanosTimeoutминус время, затраченное на ожидание при возврате из этого метода. Положительное значение может быть использовано в качестве аргумента для последующего вызова этого метода для завершения ожидания в желаемое время. Значение меньше или равно нулю указывает, что время больше не осталось. - Исключения:
-
InterruptedException- если текущий поток прерван (и поддерживается прерывание приостановки потока)
await
boolean await(long time, TimeUnit unit) throws InterruptedException
awaitNanos(unit.toNanos(time)) > 0
- Параметры:
-
time- максимальное время ожидания -
unit- единица измерения времени для аргументаtime - Возвращает:
-
falseесли время ожидания истекло до возврата из метода, иначеtrue - Исключения:
-
InterruptedException- если текущий поток прерван (и поддерживается прерывание приостановки потока)
awaitUntil
boolean awaitUntil(Date deadline) throws InterruptedException
Связанный с этим условием замок атомарно освобождается, и текущая нить отключается для целей планирования потоков и находится в спящем режиме до тех пор, пока не произойдёт одно из пяти событий:
- Другая нить вызывает метод
signal()для этогоConditionи текущая нить случайно выбирается в качестве нити, которая должна быть разбужена; или - Другая нить вызывает метод
signalAll()для этогоCondition; или - Другая нить прерывает текущую нить, и прерывание приостановки нити поддерживается; или
- Истекает указанное время ожидания; или
- Происходит «ложное пробуждение».
Во всех случаях, прежде чем этот метод сможет вернуть значение, текущая нить должна повторно получить замок, связанный с этим условием. Когда нить возвращается, она гарантированно удерживает этот замок.
Если текущая нить:
- имеет установленный статус прерывания при входе в этот метод; или
- была прервана во время ожидания, и прерывание приостановки нити поддерживается,
InterruptedException, и статус прерывания текущей нити сбрасывается. В первом случае не определено, происходит ли проверка на прерывание до или после освобождения замка. Значение возврата указывает, истекло ли время ожидания, что может использоваться следующим образом:
boolean aMethod(Date deadline)
throws InterruptedException {
boolean stillWaiting = true;
lock.lock();
try {
while (!conditionBeingWaitedFor()) {
if (!stillWaiting)
return false;
stillWaiting = theCondition.awaitUntil(deadline);
}
// ...
return true;
} finally {
lock.unlock();
}
} Соображения по реализации
Предполагается, что текущая нить удерживает замок, связанный с этим Condition при вызове этого метода. Реализация должна определить, так ли это на самом деле, и если нет, как реагировать. Как правило, будет выброшено исключение (такое как IllegalMonitorStateException), и реализация должна это задокументировать.
Реализация может отдавать приоритет реагированию на прерывание над нормальным возвратом метода в ответ на сигнал или над указанием истечения указанного времени ожидания. В любом случае реализация должна гарантировать, что сигнал перенаправляется на другую ожидающую нить, если такая имеется.
- Parameters:
-
deadline- абсолютное время ожидания - Returns:
-
falseесли время ожидания истекло при возврате, иначеtrue - Throws:
-
InterruptedException- если текущая нить прервана (и прерывание приостановки нити поддерживается)
signal
void signal()
Если какие-либо нити ожидают этого условия, то одна из них выбирается для пробуждения. Затем эта нить должна повторно получить замок перед возвратом из await.
Соображения по реализации
Реализация может (и, как правило, требует), чтобы текущая нить удерживала замок, связанный с этим
Condition при вызове этого метода. Реализации должны документировать это предположение и любые действия, выполняемые, если замок не удерживается. Как правило, будет выброшено исключение типа IllegalMonitorStateException.
signalAll
void signalAll()
Если какие-либо нити ожидают этого условия, то они все разбуживаются. Каждая нить должна повторно получить замок, прежде чем сможет вернуть значение из await.
Соображения по реализации
Реализация может (и, как правило, требует), чтобы текущая нить удерживала замок, связанный с этим
Condition при вызове этого метода. Реализации должны документировать это предположение и любые действия, выполняемые, если замок не удерживается. Как правило, будет выброшено исключение типа IllegalMonitorStateException.
© 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/locks/Condition.html