Интерфейс 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 всегда следует ожидать в цикле, проверяя предикат состояния, которого ожидается.
Реализация может исключить возможность ложных пробуждений, но рекомендуется, чтобы программисты приложений всегда предполагали, что они могут произойти, и поэтому всегда ожидают в цикле.
Три формы ожидания условной переменной (прерывистое, непрерывистое и временное) могут отличаться по лёгкости реализации на некоторых платформах и по производительности. В частности, может быть сложно обеспечить эти функции и поддерживать определённые семантики, такие как гарантии порядка.
Кроме того, возможность прерывания фактической приостановки потока может не всегда быть возможной для реализации на всех платформах.
Следовательно, реализация не обязана определять точно такие же гарантии или семантику для всех трёх форм ожидания, и она не обязана поддерживать прерывание фактической приостановки потока.
Реализация должна чётко документировать семантику и гарантии, предоставляемые каждым из методов ожидания, и когда реализация поддерживает прерывание приостановки потока, она должна соблюдать семантику прерывания, как определено в этом интерфейсе.
Поскольку прерывание, как правило, подразумевает отмену, и проверки на прерывание часто редки, реализация может отдавать приоритет реакции на прерывание перед обычным возвратом метода. Это верно, даже если можно показать, что прерывание произошло после другого действия, которое могло разблокировать поток. Реализация должна документировать это поведение.
- С:
- 1.5
Методы
| Модификатор и тип | Метод | Описание |
|---|---|---|
void |
await() |
Принуждает текущий поток ждать, пока он не получит сигнал или прервётся. |
boolean |
await(long time,
TimeUnit unit) |
Принуждает текущий поток ждать, пока он не получит сигнал или не прервётся, или пока не истечёт указанное время ожидания. |
long |
awaitNanos(long nanosTimeout) |
Принуждает текущий поток ждать, пока он не получит сигнал или не прервётся, или пока не истечёт указанное время ожидания. |
void |
awaitUninterruptibly() |
Принуждает текущий поток ждать, пока он не получит сигнал. |
boolean |
awaitUntil(Date deadline) |
Принуждает текущий поток ждать, пока он не получит сигнал или не прервётся, или пока не истечёт указанная дата завершения. |
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), и реализация должна документально подтвердить этот факт.
Реализация может отдавать приоритет реагированию на прерывание перед обычным возвратом метода в ответ на сигнал или перед указанием истечения указанного крайнего срока. В любом случае реализация должна убедиться, что сигнал перенаправлен другому ожидающему потоку, если таковой есть.
- Параметры:
-
deadline- абсолютное время ожидания до - Возвращаемое значение:
-
falseесли крайний срок истек при возврате, иначеtrue - Исключения:
-
InterruptedException- если текущий поток прерван (и прерывание приостановки потока поддерживается)
signal
void signal()
Разбудит одну ожидающую нить.
Если какие-либо нити ожидают на этой условии, то одна из них выбирается для пробуждения. Эта нить должна повторно получить блокировку перед возвратом из await.
Соображения по реализации
Реализация может (и обычно требует), чтобы текущая нить удерживала блокировку, связанную с этим
Condition при вызове этого метода. Реализации должны документировать это предварительное условие и любые действия, предпринимаемые, если блокировка не удерживается. Обычно выбрасывается исключение, такое как IllegalMonitorStateException.
signalAll
void signalAll()
Разбудит все ожидающие нити.
Если какие-либо нити ожидают на этом условии, то все они будут разбужены. Каждая нить должна повторно получить блокировку перед возвратом из await.
Соображения по реализации
Реализация может (и обычно требует), чтобы текущая нить удерживала блокировку, связанную с этим
Condition при вызове этого метода. Реализации должны документировать это предварительное условие и любые действия, предпринимаемые, если блокировка не удерживается. Обычно выбрасывается исключение, такое как IllegalMonitorStateException.
© 1993, 2020, 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/11/docs/api/java.base/java/util/concurrent/locks/Condition.html