Spec-Zone.ru › OpenJDK 25

Класс 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

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

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, 2025, 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.
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html

Spec-Zone.ru

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