Интерфейс 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 {
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(Object 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 Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
} (Класс ArrayBlockingQueue предоставляет эту функциональность, поэтому нет необходимости реализовывать этот пример класса использования.) Реализация Condition может предоставлять поведение и семантику, отличную от методов монитора Object, например, гарантированный порядок уведомлений или отсутствие необходимости удерживать замок при выполнении уведомлений. Если реализация предоставляет такую специализированную семантику, то эта реализация должна документировать эту семантику.
Обратите внимание, что экземпляры Condition — это обычные объекты, которые сами могут быть использованы в качестве целевого объекта в операторе synchronized, и для них могут быть вызваны свои собственные методы монитора wait и notification. Получение замка монитора экземпляра Condition, или использование его методов монитора, не имеет заданной связи с приобретением Lock, связанным с этим экземпляром Condition, или с использованием его методов ожидания и сигнализации. Рекомендуется избегать использования экземпляров Condition таким образом, за исключением, возможно, их собственной реализации.
Если не указано иное, передача значения null для любого параметра приведет к выбрасыванию NullPointerException.
Рекомендации по реализации
При ожидании на Condition, в общем случае, допускается «ложное пробуждение», как уступка семантике базовой платформы. Это оказывает незначительное практическое влияние на большинство приложений, так как 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) {
long nanos = unit.toNanos(timeout);
lock.lock();
try {
while (!conditionBeingWaitedFor()) {
if (nanos <= 0L)
return false;
nanos = theCondition.awaitNanos(nanos);
}
// ...
} 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) {
boolean stillWaiting = true;
lock.lock();
try {
while (!conditionBeingWaitedFor()) {
if (!stillWaiting)
return false;
stillWaiting = theCondition.awaitUntil(deadline);
}
// ...
} 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.