Spec-Zone.ru › OpenJDK 27

Класс ScopedValue<T>

java.lang.Object
java.lang.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, чтобы оно было доступно только коду одного класса (или гнезда).

Повторная привязка

API ScopedValue позволяет устанавливать новую связь для вложенных динамических областей видимости. Это называется повторной привязкой. 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 можно использовать для передачи данных методу без параметров, у такого подхода есть несколько недостатков:
  1. ThreadLocal не препятствует удалённому вызываемому коду устанавливать новое значение.
  2. У ThreadLocal неограниченный срок жизни, поэтому оно сохраняет значение и после завершения метода, если его явно не удалить.
  3. Наследование требует значительных затрат: при создании каждого дочернего потока необходимо копировать карту локальных переменных потока и их значений.
Примечание по реализации:
Значения области видимости предназначены для использования в относительно небольшом количестве. Метод 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<T> newInstance()
Создаёт значение области видимости, изначально не связанное ни с одним потоком.
T orElse(T other)
Возвращает значение области видимости, если оно связано с текущим потоком; в противном случае возвращает other.
<X extends Throwable>
T
orElseThrow(Supplier<? extends X> exceptionSupplier)
Возвращает значение области видимости, если оно связано с текущим потоком; в противном случае выбрасывает исключение, созданное функцией, предоставляющей исключение.
static <T> ScopedValue.Carrier where(ScopedValue<T> key, T value)
Создаёт новый Carrier с единственным соответствием между ключом ScopedValue и значением.

Методы, объявленные в классе Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Модификатор и тип Метод Описание
protected Object clone()
Создаёт и возвращает копию этого объекта.
boolean equals(Object obj)
Указывает, «равен ли» этот объект другому объекту.
protected void finalize()
Устарело, будет удалено: этот элемент API подлежит удалению в будущей версии.
Финализация устарела и будет удалена в одном из будущих выпусков.
final Class<?> getClass()
Возвращает класс времени выполнения этого Object.
int hashCode()
Возвращает хеш-код этого объекта.
final void notify()
Пробуждает один поток, ожидающий на мониторе этого объекта.
final void notifyAll()
Пробуждает все потоки, ожидающие на мониторе этого объекта.
String toString()
Возвращает строковое представление объекта.
final void wait()
Переводит текущий поток в ожидание до пробуждения, обычно вследствие уведомления или прерывания.
final void wait(long timeoutMillis)
Переводит текущий поток в ожидание до пробуждения, обычно вследствие уведомления или прерывания, либо до истечения заданного промежутка реального времени.
final void wait(long timeoutMillis, int nanos)
Переводит текущий поток в ожидание до пробуждения, обычно вследствие уведомления или прерывания, либо до истечения заданного промежутка реального времени.

Подробное описание методов

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 — если значение области видимости не связано с текущим потоком

Сообщить об ошибке или предложить улучшение
Дополнительную справочную информацию по API и документацию для разработчиков см. в документации Java SE, содержащей более подробные описания для разработчиков, включая концептуальные обзоры, определения терминов, обходные решения и работающие примеры кода. Другие версии.
Java является товарным знаком или зарегистрированным товарным знаком Oracle и/или её дочерних компаний в США и других странах.
Авторские права © 1993, 2026, Oracle и/или её дочерние компании, 500 Oracle Parkway, Redwood Shores, CA 94065 USA.
Все права защищены. Использование регулируется условиями лицензии и политикой распространения документации.

© 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API