Класс ScopedValue<T>
- Параметры типа:
T— тип значения
public final class ScopedValue<T> extends Object
В языке программирования Java данные обычно передаются методу с помощью параметра метода. Чтобы попасть в метод, использующий эти данные, их может потребоваться передать через цепочку множества методов. Каждый метод в цепочке вызовов должен объявить параметр, и каждый метод имеет доступ к данным. ScopedValue предоставляет возможность передавать данные удалённому методу (обычно обратному вызову) без использования параметров методов. Фактически ScopedValue — это неявный параметр метода. Это «как если бы» у каждого метода в цепочке вызовов был дополнительный параметр. Ни один из методов не объявляет этот параметр, и только методы, имеющие доступ к объекту ScopedValue, могут получить доступ к его значению (данным). ScopedValue позволяет безопасно передавать данные от вызывающего кода удалённому вызываемому коду через цепочку промежуточных методов, которые не объявляют параметр для этих данных и не имеют к ним доступа.
API ScopedValue работает, выполняя метод с объектом ScopedValue, связанным с некоторым значением на ограниченный период выполнения метода. Метод может вызвать другой метод, который, в свою очередь, может вызвать ещё один. Последовательное выполнение методов определяет динамическую область видимости. Код в этих методах, имеющий доступ к объекту ScopedValue, может прочитать его значение. Объект ScopedValue снова становится несвязанным, когда исходный метод завершается обычным образом или с исключением. API ScopedValue поддерживает выполнение Runnable или ScopedValue.CallableOp с объектом ScopedValue, связанным со значением.
Рассмотрим следующий пример: значение области видимости «NAME» связано со значением «duke» на время выполнения метода run объекта Runnable. Метод run, в свою очередь, вызывает метод doSomething.
private static final ScopedValue<String> NAME = ScopedValue.newInstance();
ScopedValue.where(NAME, "duke").run(() -> doSomething());
doSomething и имеющий доступ к полю NAME, может вызвать NAME.get(), чтобы прочитать значение «duke».
NAME связан во время выполнения метода run. Он снова становится несвязанным, когда метод run завершается. В примере с использованием run вызывается метод, который не возвращает результат. Метод call можно использовать для вызова метода, возвращающего результат. ScopedValue определяет метод where(ScopedValue, Object) для случаев, когда несколько соответствий (ScopedValue значению) накапливаются до вызова метода, которому передаются все ScopedValue, связанные со своими значениями.
Связи относятся к отдельным потокам
СвязьScopedValue со значением относится к отдельному потоку. Вызов run выполняет метод, в котором ScopedValue связано со значением для текущего потока. Метод get возвращает значение, связанное с текущим потоком. В этом примере, если код, выполняемый одним потоком, вызывает:
ScopedValue.where(NAME, "duke1").run(() -> doSomething());
ScopedValue.where(NAME, "duke2").run(() -> doSomething());
doSomething (или любом вызываемом им методе), вызывающий NAME.get(), прочитает значение «duke1» или «duke2» — в зависимости от того, какой поток выполняется. Значения области видимости как возможности доступа
ОбъектScopedValue следует рассматривать как возможность доступа или ключ для доступа к его значению, когда ScopedValue связано. Безопасность использования зависит от управления доступом (см. Спецификацию виртуальной машины Java, раздел §5.4.4) и осторожного обращения с объектом ScopedValue, чтобы не делиться им. Во многих случаях
ScopedValue объявляется в поле final и static, чтобы оно было доступно только коду одного класса (или гнезда). Повторная привязка
APIScopedValue позволяет устанавливать новую связь для вложенных динамических областей видимости. Это называется повторной привязкой. ScopedValue, связанное со значением, может быть связано с новым значением на время ограниченного выполнения нового метода. Последовательное выполнение кода, выполняемого этим методом, определяет вложенную динамическую область видимости. После завершения метода значение ScopedValue возвращается к предыдущему. В приведённом выше примере предположим, что код, выполняемый методом doSomething, связывает NAME с новым значением следующим образом:
ScopedValue.where(NAME, "duchess").run(() -> doMore());
doMore(), который вызывает
NAME.get(), прочитает значение «duchess». После завершения doMore() значение NAME возвращается к «duke». Наследование
ScopedValue поддерживает совместное использование между потоками. Это совместное использование ограничено структурированными случаями, когда дочерние потоки запускаются и завершаются в пределах ограниченного периода выполнения родительского потока. При использовании StructuredTaskScopeПРЕДВАРИТЕЛЬНАЯ ВЕРСИЯ связи значений области видимости сохраняются при создании StructuredTaskScope и наследуются всеми потоками, запускаемыми в этой области задач с помощью метода forkПРЕДВАРИТЕЛЬНАЯ ВЕРСИЯ. Если ScopedValue используется совместно несколькими потоками, значение должно быть неизменяемым объектом либо весь доступ к значению должен соответствующим образом синхронизироваться.
В следующем примере ScopedValue NAME связано со значением «duke» на время выполнения исполняемой операции. Код метода
run создаёт StructuredTaskScope, запускающий три задачи. Код, выполняемый прямо или косвенно этими потоками при запуске childTask1(), childTask2() и childTask3(), который вызывает NAME.get(), прочитает значение «duke».
private static final ScopedValue<String> NAME = ScopedValue.newInstance();
ScopedValue.where(NAME, "duke").run(() -> {
try (var scope = StructuredTaskScope.open()) {
scope.fork(() -> childTask1());
scope.fork(() -> childTask2());
scope.fork(() -> childTask3());
scope.join();
..
}
});
Если не указано иное, передача аргумента null методу этого класса приводит к выбросу NullPointerException.
- Примечание API:
- В случаях, когда требуется «односторонняя передача» данных без использования параметров методов, следует предпочесть
ScopedValueвместоThreadLocal. ХотяThreadLocalможно использовать для передачи данных методу без параметров, у такого подхода есть несколько недостатков:-
ThreadLocalне препятствует удалённому вызываемому коду устанавливать новое значение. - У
ThreadLocalнеограниченный срок жизни, поэтому оно сохраняет значение и после завершения метода, если его явно не удалить. - Наследование требует значительных затрат: при создании каждого дочернего потока необходимо копировать карту локальных переменных потока и их значений.
-
- Примечание по реализации:
- Значения области видимости предназначены для использования в относительно небольшом количестве. Метод
get()сначала выполняет поиск во внешних областях видимости, чтобы найти ближайшую связь значения области видимости. Затем результат поиска кэшируется в небольшом кэше локальных переменных потока. Последующие вызовыget()для этого значения области видимости почти всегда будут очень быстрыми. Однако если программа циклически использует множество значений области видимости, процент попаданий в кэш будет низким, а производительность — неудовлетворительной. Такая конструкция позволяет очень быстро наследовать значения области видимости потокамStructuredTaskScopeПРЕДВАРИТЕЛЬНАЯ ВЕРСИЯ: по сути, достаточно скопировать указатель; для выхода из связи значения области видимости также требуется лишь обновить указатель.Поскольку кэш значений области видимости для каждого потока невелик, клиентам следует свести к минимуму количество используемых связанных значений. Например, если таким способом необходимо передать несколько значений, имеет смысл создать класс-запись для их хранения, а затем связать одно
ScopedValueс экземпляром этой записи.В этой версии эталонная реализация предоставляет несколько системных свойств для настройки производительности значений области видимости.
Системное свойство
java.lang.ScopedValue.cacheSizeуправляет размером кэша значений области видимости для каждого потока. Этот кэш крайне важен для производительности значений области видимости. Если он слишком мал, библиотеке времени выполнения придётся снова и снова выполнять поиск при каждом вызовеget(). Если он слишком велик, память будет расходоваться без необходимости. Размер кэша значений области видимости по умолчанию — 16 записей. Его размер можно изменить в диапазоне от 2 до 16 записей. ЗначениеScopedValue.cacheSizeдолжно быть целой степенью числа 2.Например, можно использовать
-Djava.lang.ScopedValue.cacheSize=8.Другое системное свойство —
jdk.preserveScopedValueCache. Оно определяет, сохраняется ли кэш значений области видимости для каждого потока, когда виртуальный поток заблокирован. По умолчанию это свойство имеет значениеtrue, то есть каждый виртуальный поток сохраняет свой кэш значений области видимости во время блокировки. Как и в случае сScopedValue.cacheSize, здесь приходится выбирать между объёмом памяти и скоростью: если многие виртуальные потоки большую часть времени заблокированы, установка значенияfalseможет заметно сэкономить память, но после блокирующей операции кэш значений области видимости каждого виртуального потока потребуется создать заново. - Начиная с версии:
- 25
Краткое описание вложенных классов
| Модификатор и тип | Класс | Описание |
|---|---|---|
static interface |
ScopedValue.CallableOp<T, X extends Throwable> |
Операция, возвращающая результат и способная выбросить исключение. |
static final class |
ScopedValue.Carrier |
Соответствие значений области видимости, выступающих в роли ключей, значениям. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
T |
get() |
Возвращает значение области видимости, если оно связано с текущим потоком. |
boolean |
isBound() |
Возвращает true, если это значение области видимости связано с текущим потоком. |
static <T> ScopedValue |
newInstance() |
Создаёт значение области видимости, изначально не связанное ни с одним потоком. |
T |
orElse |
Возвращает значение области видимости, если оно связано с текущим потоком; в противном случае возвращает other. |
<X extends Throwable> |
orElseThrow |
Возвращает значение области видимости, если оно связано с текущим потоком; в противном случае выбрасывает исключение, созданное функцией, предоставляющей исключение. |
static <T> ScopedValue.Carrier |
where |
Создаёт новый Carrier с единственным соответствием между ключом ScopedValue и значением. |
Методы, объявленные в классе Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait | Модификатор и тип | Метод | Описание |
|---|---|---|
protected Object |
clone() |
Создаёт и возвращает копию этого объекта. |
boolean |
equals |
Указывает, «равен ли» этот объект другому объекту. |
protected void |
finalize() |
Устарело, будет удалено: этот элемент API подлежит удалению в будущей версии. Финализация устарела и будет удалена в одном из будущих выпусков. |
final Class |
getClass() |
Возвращает класс времени выполнения этого Object. |
int |
hashCode() |
Возвращает хеш-код этого объекта. |
final void |
notify() |
Пробуждает один поток, ожидающий на мониторе этого объекта. |
final void |
notifyAll() |
Пробуждает все потоки, ожидающие на мониторе этого объекта. |
String |
toString() |
Возвращает строковое представление объекта. |
final void |
wait() |
Переводит текущий поток в ожидание до пробуждения, обычно вследствие уведомления или прерывания. |
final void |
wait |
Переводит текущий поток в ожидание до пробуждения, обычно вследствие уведомления или прерывания, либо до истечения заданного промежутка реального времени. |
final void |
wait |
Переводит текущий поток в ожидание до пробуждения, обычно вследствие уведомления или прерывания, либо до истечения заданного промежутка реального времени. |
Подробное описание методов
where
public static <T> ScopedValue.Carrier where(ScopedValue<T> key, T value)
Carrier с единственным соответствием между ключом ScopedValue и значением. Carrier можно использовать для накопления соответствий, чтобы выполнить операцию со всеми значениями области видимости, связанными со значениями из этих соответствий. В следующем примере выполняется операция, в которой k1 связано (или повторно связано) с v1, а k2 связано (или повторно связано) с v2. ScopedValue.where(k1, v1).where(k2, v2).run(() -> ... );
- Параметры типа:
T— тип значения- Параметры:
-
key— ключScopedValue -
value— значение; может бытьnull - Возвращает:
- новый
Carrierс единственным соответствием
newInstance
public static <T> ScopedValue<T> newInstance()
- Параметры типа:
T— тип значения- Возвращает:
- новый
ScopedValue
get
public T get()
- Возвращает:
- значение области видимости, если оно связано с текущим потоком
- Выбрасывает:
-
NoSuchElementException— если значение области видимости не связано
isBound
public boolean isBound()
true, если это значение области видимости связано с текущим потоком.- Возвращает:
-
true, если это значение области видимости связано с текущим потоком
orElse
public T orElse(T other)
other.- Параметры:
-
other— значение, возвращаемое, если связь отсутствует - Возвращает:
- значение области видимости, если оно связано; в противном случае —
other
orElseThrow
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
- Параметры типа:
X— тип исключения, которое может быть выброшено- Параметры:
-
exceptionSupplier— функция, создающая исключение для выбрасывания - Возвращает:
- значение области видимости, если оно связано с текущим потоком
- Выбрасывает:
-
X— если значение области видимости не связано с текущим потоком
© 1993, 2025, 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.