Интерфейс 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.
Рекомендации по реализации
При ожидании на Condition, в общем случае допускается «ложное пробуждение», как уступка семантике базовой платформы. Это оказывает незначительное практическое влияние на большинство приложений, поскольку на Condition всегда нужно ждать в цикле, проверяя предикат состояния, которого ждут. Реализация может исключить возможность ложных пробуждений, но рекомендуется, чтобы разработчики приложений всегда предполагали, что они могут произойти, и поэтому всегда ждали в цикле.
Три формы ожидания условия (прерывистое, непрерывистое и по таймеру) могут отличаться по сложности реализации на некоторых платформах и по своим характеристикам производительности. В частности, может быть трудно обеспечить эти функции и сохранить определенную семантику, такую как гарантии порядка. Кроме того, возможность прерывания фактической приостановки потока может быть не всегда реализуемой на всех платформах.
Следовательно, от реализации не требуется определение точно одинаковых гарантий или семантики для всех трех форм ожидания, а также не требуется поддержка прерывания фактической приостановки потока.
Реализация должна четко документировать семантику и гарантии, предоставляемые каждым из методов ожидания, и когда реализация поддерживает прерывание приостановки потока, она должна соблюдать семантику прерывания, как определено в этом интерфейсе.
Поскольку прерывание, как правило, подразумевает отмену, а проверки на прерывание часто нечасты, реализация может отдавать предпочтение реагированию на прерывание перед обычным возвратом метода. Это верно даже если можно показать, что прерывание произошло после другого действия, которое могло разблокировать поток. Реализация должна документировать это поведение.
- С:
- 1.5
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
void |
await() |
Принуждает текущий поток ожидать, пока он не получит сигнал или не будет прерван. |
boolean |
await |
Принуждает текущий поток ожидать, пока он не получит сигнал или не будет прерван, или не истечет заданное время ожидания. |
long |
awaitNanos |
Принуждает текущий поток ожидать, пока он не получит сигнал или не будет прерван, или не истечет заданное время ожидания. |
void |
awaitUninterruptibly() |
Принуждает текущий поток ожидать, пока он не получит сигнал. |
boolean |
awaitUntil |
Принуждает текущий поток ожидать, пока он не получит сигнал или не будет прерван, или не истечет заданный срок. |
void |
signal() |
Разбуживает один ожидающий поток. |
void |
signalAll() |
Разбуживает все ожидающие потоки. |
Подробное описание методов
await
void await() throws InterruptedException
Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток отключается от планирования потоков и находится в спящем режиме, пока не произойдёт одно из четырёх событий:
- Другой поток вызывает метод
signal()для этого условия, и текущий поток выбирается для пробуждения; или - Другой поток вызывает метод
signalAll()для этого условия; или - Другой поток прерывает текущий поток, и прерывание приостановки потока поддерживается; или
- Происходит «ложное пробуждение».
Во всех случаях, прежде чем метод сможет вернуть результат, текущий поток должен повторно получить блокировку, связанную с этим условием. Когда поток возвращается, он гарантированно держит эту блокировку.
Если у текущего потока:
- статус прерывания установлен при входе в этот метод; или
- происходит прерывание во время ожидания, и прерывание приостановки потока поддерживается,
InterruptedException, и статус прерывания текущего потока сбрасывается. В первом случае не определено, происходит ли проверка на прерывание до освобождения блокировки. Рекомендации по реализации
Предполагается, что текущий поток владеет блокировкой, связанной с этим условием, когда вызывается этот метод. Реализация должна определить, так ли это на самом деле, и если нет, то как реагировать. Обычно выбрасывается исключение (такое как IllegalMonitorStateException), и реализация должна это задокументировать.
Реализация может отдавать приоритет обработке прерывания по сравнению с нормальным возвратом метода в ответ на сигнал. В этом случае реализация должна гарантировать, что сигнал перенаправляется другому ожидающему потоку, если таковой есть.
- Исключение:
-
InterruptedException- если текущий поток прерван (и прерывание приостановки потока поддерживается)
awaitUninterruptibly
void awaitUninterruptibly()
Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток отключается от планирования потоков и находится в спящем режиме, пока не произойдёт одно из трёх событий:
- Другой поток вызывает метод
signal()для этого условия, и текущий поток выбирается для пробуждения; или - Другой поток вызывает метод
signalAll()для этого условия; или - Происходит «ложное пробуждение».
Во всех случаях, прежде чем метод сможет вернуть результат, текущий поток должен повторно получить блокировку, связанную с этим условием. Когда поток возвращается, он гарантированно держит эту блокировку.
Если у текущего потока установлен статус прерывания при входе в этот метод или он прерывается во время ожидания, он продолжит ожидание до получения сигнала. После возврата из этого метода статус прерывания останется установленным.
Рекомендации по реализации
Предполагается, что текущий поток владеет блокировкой, связанной с этим условием, когда вызывается этот метод. Реализация должна определить, так ли это на самом деле, и если нет, то как реагировать. Обычно выбрасывается исключение (такое как IllegalMonitorStateException), и реализация должна это задокументировать.
awaitNanos
long awaitNanos(long nanosTimeout) throws InterruptedException
Блокировка, связанная с этим условием, атомарно освобождается, и текущий поток отключается от планирования потоков и находится в спящем режиме, пока не произойдёт одно из пяти событий:
- Другой поток вызывает метод
signal()для этого условия, и текущий поток выбирается для пробуждения; или - Другой поток вызывает метод
signalAll()для этого условия; или - Другой поток прерывает текущий поток, и прерывание приостановки потока поддерживается; или
- Истекает указанное время ожидания; или
- Происходит «ложное пробуждение».
Во всех случаях, прежде чем метод сможет вернуть результат, текущий поток должен повторно получить блокировку, связанную с этим условием. Когда поток возвращается, он гарантированно держит эту блокировку.
Если у текущего потока:
- статус прерывания установлен при входе в этот метод; или
- происходит прерывание во время ожидания, и прерывание приостановки потока поддерживается,
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();
}
} Примечание к проекту: Этот метод требует аргумент в наносекундах, чтобы избежать ошибок усечения при отслеживании оставшегося времени. Такая потеря точности затруднила бы программистам обеспечение того, чтобы общее время ожидания не было систематически короче заданного, когда происходят повторные ожидания.
Рекомендации по реализации
Предполагается, что текущий поток владеет блокировкой, связанной с этим условием, когда вызывается этот метод. Реализация должна определить, так ли это на самом деле, и если нет, то как реагировать. Обычно выбрасывается исключение (такое как 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, 2023, 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/21/docs/api/java.base/java/util/concurrent/locks/Condition.html