Preview
Third Preview of Scoped Values (JEP 481).
Programs can only use ScopedValue
when preview features are enabled.
Preview features may be removed in a future release,
or upgraded to permanent features of the Java platform.
In the Java programming language, data is usually passed to a method by means of a
method parameter. The data may need to be passed through a sequence of many methods to
get to the method that makes use of the data. Every method in the sequence of calls
needs to declare the parameter and every method has access to the data.
ScopedValue
provides a means to pass data to a faraway method (typically a
callback) without using method parameters. In effect, a ScopedValue
is an implicit method parameter. It is "as if" every method in a sequence of
calls has an additional parameter. None of the methods declare the parameter and only
the methods that have access to the ScopedValue
object can access its value
(the data). ScopedValue
makes it possible to securely pass data from a
caller to a faraway callee through a sequence of intermediate methods
that do not declare a parameter for the data and have no access to the data.
The ScopedValue
API works by executing a method with a ScopedValue
object bound to some value for the bounded period of execution of a method.
The method may invoke another method, which in turn may invoke another. The unfolding
execution of the methods define a dynamic scope. Code in these methods with
access to the ScopedValue
object may read its value. The ScopedValue
object reverts to being unbound when the original method completes normally or
with an exception. The ScopedValue
API supports executing a Runnable
,
or CallableOp
with a ScopedValue
bound to a value.
Consider the following example with a scoped value "NAME
" bound to the value
"duke
" for the execution of a Runnable
's run
method.
The run
method, in turn, invokes a method doSomething
.
private static final ScopedValue<String> NAME = ScopedValue.newInstance(); ScopedValue.runWhere(NAME, "duke", () -> doSomething());
doSomething
, with access to the field
NAME
, can invoke NAME.get()
to read the value "duke
". NAME
is bound while executing the run
method. It reverts to being unbound when
the run
method completes.
The example using runWhere
invokes a method that does not return a result.
The callWhere
method can be used
to invoke a method that returns a result.
In addition, ScopedValue
defines the where(ScopedValue, Object)
method
for cases where multiple mappings (of ScopedValue
to value) are accumulated
in advance of calling a method with all ScopedValue
s bound to their value.
ScopedValue
binding to a value is per-thread. Invoking xxxWhere
executes a method with a ScopedValue
bound to a value for the current thread.
The get
method returns the value bound for the current thread.
In the example, if code executed by one thread invokes this:
ScopedValue.runWhere(NAME, "duke1", () -> doSomething());
ScopedValue.runWhere(NAME, "duke2", () -> doSomething());
doSomething
(or any method that it calls) invoking NAME.get()
will read the value "duke1
" or "duke2
", depending on which thread is
executing.
ScopedValue
object should be treated as a capability or a key to
access its value when the ScopedValue
is bound. Secure usage depends on access
control (see The Java Virtual Machine Specification, Section 5.4.4)
and taking care to not share the ScopedValue
object. In many cases, a ScopedValue
will be declared in a final
and static
field so that it
is only accessible to code in a single class (or nest).
ScopedValue
API allows a new binding to be established for nested
dynamic scopes. This is known as rebinding. A ScopedValue
that
is bound to a value may be bound to a new value for the bounded execution of a new
method. The unfolding execution of code executed by that method defines the nested
dynamic scope. When the method completes, the value of the ScopedValue
reverts
to its previous value.
In the above example, suppose that code executed by doSomething
binds
NAME
to a new value with:
ScopedValue.runWhere(NAME, "duchess", () -> doMore());
doMore()
that invokes NAME.get()
will read the value "duchess
". When doMore()
completes
then the value of NAME
reverts to "duke
".
ScopedValue
supports sharing across threads. This sharing is limited to
structured cases where child threads are started and terminate within the bounded
period of execution by a parent thread. When using a StructuredTaskScope
,
scoped value bindings are captured when creating a StructuredTaskScope
and inherited by all threads started in that task scope with the
fork
method.
A ScopedValue
that is shared across threads requires that the value be an
immutable object or for all access to the value to be appropriately synchronized.
In the following example, the ScopedValue
NAME
is bound to the
value "duke
" for the execution of a runnable operation. The code in the run
method creates a StructuredTaskScope
that forks three tasks. Code executed
directly or indirectly by these threads running childTask1()
, childTask2()
,
and childTask3()
that invokes NAME.get()
will read the value
"duke
".
private static final ScopedValue<String> NAME = ScopedValue.newInstance(); ScopedValue.runWhere(NAME, "duke", () -> { try (var scope = new StructuredTaskScope<String>()) { scope.fork(() -> childTask1()); scope.fork(() -> childTask2()); scope.fork(() -> childTask3()); ... } });
Unless otherwise specified, passing a null
argument to a method in this
class will cause a NullPointerException
to be thrown.
API Note
A ScopedValue
should be preferred over a ThreadLocal
for cases where
the goal is "one-way transmission" of data without using method parameters. While a
ThreadLocal
can be used to pass data to a method without using method parameters,
it does suffer from a number of issues:
ThreadLocal
does not prevent code in a faraway callee from setting a new value.
ThreadLocal
has an unbounded lifetime and thus continues to have a value
after a method completes, unless explicitly removed.
Implementation Note
Scoped values are designed to be used in fairly small
numbers. get
initially performs a search through enclosing
scopes to find a scoped value's innermost binding. It
then caches the result of the search in a small thread-local
cache. Subsequent invocations of get
for that scoped value
will almost always be very fast. However, if a program has many
scoped values that it uses cyclically, the cache hit rate
will be low and performance will be poor. This design allows
scoped-value inheritance by StructuredTaskScope
threads to
be very fast: in essence, no more than copying a pointer, and
leaving a scoped-value binding also requires little more than
updating a pointer.
Because the scoped-value per-thread cache is small, clients
should minimize the number of bound scoped values in use. For
example, if it is necessary to pass a number of values in this way,
it makes sense to create a record class to hold those values, and
then bind a single ScopedValue
to an instance of that record.
For this release, the reference implementation provides some system properties to tune the performance of scoped values.
The system property java.lang.ScopedValue.cacheSize
controls the size of the (per-thread) scoped-value cache. This cache is crucial
for the performance of scoped values. If it is too small,
the runtime library will repeatedly need to scan for each
get
. If it is too large, memory will be unnecessarily
consumed. The default scoped-value cache size is 16 entries. It may
be varied from 2 to 16 entries in size. ScopedValue.cacheSize
must be an integer power of 2.
For example, you could use -Djava.lang.ScopedValue.cacheSize=8
.
The other system property is jdk.preserveScopedValueCache
.
This property determines whether the per-thread scoped-value
cache is preserved when a virtual thread is blocked. By default
this property is set to true
, meaning that every virtual
thread preserves its scoped-value cache when blocked. Like ScopedValue.cacheSize
, this is a space versus speed trade-off: in
situations where many virtual threads are blocked most of the time,
setting this property to false
might result in a useful
memory saving, but each virtual thread's scoped-value cache would
have to be regenerated after a blocking operation.
Modifier and Type | Class and Description |
---|---|
private static class | |
public static interface | ScopedValue.
result type of the operation T, type of the exception thrown by the operation X extends Throwable>
Preview
Third Preview of Scoped Values (JEP 481).
An operation that returns a result and may throw an exception.
|
public static class | ScopedValue.
Preview
Third Preview of Scoped Values (JEP 481).
A mapping of scoped values, as keys, to values.
|
pack-priv static class | ScopedValue.
An immutable map from |
Modifier and Type | Field and Description |
---|---|
private final int | |
private static final Object | |
private static int |
Access | Constructor and Description |
---|---|
private |
Modifier and Type | Method and Description |
---|---|
pack-priv int | Returns: the bitmaskReturn a bit mask that may be used to determine if this ScopedValue is bound in the current context. |
public static < the type of the value T, the result type R, type of the exception thrown by the operation X extends Throwable> R | Returns: the resultthe key, T ScopedValue keythe value, can be value, ScopedValue.null the operation to call opCalls a value-returning operation with a |
pack-priv static boolean | |
private Object | |
private static synchronized int | |
public T | Returns: the value of the scoped value if bound in the current threadReturns the value of the scoped value if bound in the current thread. |
public int | |
public boolean | Returns: true if this scoped value is bound in the current threadReturns |
public static < the type of the value T> ScopedValue | Returns: a newScopedValue Creates a scoped value that is initially unbound for all threads. |
public T | Returns: the value of the scoped value if bound, otherwiseother the value to return if not bound, can be other)null Returns the value of this scoped value if bound in the current thread, otherwise
returns |
public < the type of the exception that may be thrown X extends Throwable> T | Returns: the value of the scoped value if bound in the current threadthe supplying function that produces the exception to throw exceptionSupplier)Returns the value of this scoped value if bound in the current thread, otherwise throws an exception produced by the exception supplying function. |
public static < the type of the value T> void | runWhere(ScopedValue<T>
the key, T ScopedValue keythe value, can be value, Runnable null the operation to call op)Run an operation with a |
private static ScopedValue. | |
private static Object[] | |
private static void | |
private T | |
public static < the type of the value T> ScopedValue. | Returns: a newCarrier with a single mappingthe key, T ScopedValue keythe value, can be value)null Creates a new |
hash | back to summary |
---|---|
private final int hash |
NEW_THREAD_BINDINGS | back to summary |
---|---|
private static final Object NEW_THREAD_BINDINGS |
nextKey | back to summary |
---|---|
private static int nextKey |
ScopedValue | back to summary |
---|---|
private ScopedValue() |
bitmask | back to summary |
---|---|
pack-priv int bitmask() Return a bit mask that may be used to determine if this ScopedValue is bound in the current context. Each Carrier holds a bit mask which is the OR of all the bit masks of the bound ScopedValues.
|
callWhere | back to summary |
---|---|
public static <T, R, X extends Throwable> R callWhere(ScopedValue<T> key, T value, ScopedValue. Calls a value-returning operation with a Scoped values are intended to be used in a structured manner. If code
invoked directly or indirectly by the operation creates a Implementation Note This method is implemented to be equivalent to: ScopedValue.where(key, value).call(op);
|
containsAll | back to summary |
---|---|
pack-priv static boolean containsAll(int bitmask, int targetBits) |
findBinding | back to summary |
---|---|
private Object findBinding() Return the value of the scoped value or NIL if not bound. |
generateKey | back to summary |
---|---|
private static synchronized int generateKey() |
get | back to summary |
---|---|
public T get() Returns the value of the scoped value if bound in the current thread.
|
hashCode | back to summary |
---|---|
public int hashCode() Overrides java. Doc from java. Returns a hash code value for this object. This method is
supported for the benefit of hash tables such as those provided by
The general contract of
|
isBound | back to summary |
---|---|
public boolean isBound() Returns
|
newInstance | back to summary |
---|---|
public static <T> ScopedValue Creates a scoped value that is initially unbound for all threads.
|
orElse | back to summary |
---|---|
public T orElse(T other) Returns the value of this scoped value if bound in the current thread, otherwise
returns
|
orElseThrow | back to summary |
---|---|
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws Supplier-:X Returns the value of this scoped value if bound in the current thread, otherwise throws an exception produced by the exception supplying function.
|
runWhere | back to summary |
---|---|
public static <T> void runWhere(ScopedValue<T> key, T value, Runnable op) Run an operation with a Scoped values are intended to be used in a structured manner. If code
invoked directly or indirectly by the operation creates a Implementation Note This method is implemented to be equivalent to: ScopedValue.where(key, value).run(op);
|
scopedValueBindings | back to summary |
---|---|
private static ScopedValue. |
scopedValueCache | back to summary |
---|---|
private static Object[] scopedValueCache() |
setScopedValueCache | back to summary |
---|---|
private static void setScopedValueCache(Object[] cache) |
slowGet | back to summary |
---|---|
private T slowGet()
|
where | back to summary |
---|---|
public static <T> ScopedValue. Creates a new ScopedValue.where(k1, v1).where(k2, v2).run(() -> ... );
|
Modifier and Type | Field and Description |
---|---|
private static final int | |
pack-priv static final int | |
private static final int | |
pack-priv static final int | |
private static final int | |
pack-priv static final int | |
pack-priv static final int | |
private static final JavaUtilConcurrentTLRAccess |
Access | Constructor and Description |
---|---|
private |
Modifier and Type | Method and Description |
---|---|
private static boolean | |
private static Object | |
pack-priv static void | |
pack-priv static int | |
private static int | |
pack-priv static int | |
pack-priv static void | |
pack-priv static int | |
private static int | |
pack-priv static int | |
private static void | |
private static void | |
private static void |
CACHE_TABLE_SIZE | back to summary |
---|---|
private static final int CACHE_TABLE_SIZE |
INDEX_BITS | back to summary |
---|---|
pack-priv static final int INDEX_BITS |
MAX_CACHE_SIZE | back to summary |
---|---|
private static final int MAX_CACHE_SIZE |
PRIMARY_MASK | back to summary |
---|---|
pack-priv static final int PRIMARY_MASK |
SLOT_MASK | back to summary |
---|---|
private static final int SLOT_MASK |
TABLE_MASK | back to summary |
---|---|
pack-priv static final int TABLE_MASK |
TABLE_SIZE | back to summary |
---|---|
pack-priv static final int TABLE_SIZE |
THREAD_LOCAL_RANDOM_ACCESS | back to summary |
---|---|
private static final JavaUtilConcurrentTLRAccess THREAD_LOCAL_RANDOM_ACCESS |
Cache | back to summary |
---|---|
private Cache() |
chooseVictim | back to summary |
---|---|
private static boolean chooseVictim() |
getKey | back to summary |
---|---|
private static Object getKey(Object[] objs, int n) |
invalidate | back to summary |
---|---|
pack-priv static void invalidate(int toClearBits) |
primaryIndex | back to summary |
---|---|
pack-priv static int primaryIndex(ScopedValue<?> key) |
primarySlot | back to summary |
---|---|
private static int primarySlot(ScopedValue<?> key) |
primarySlot | back to summary |
---|---|
pack-priv static int primarySlot(int hash) |
put | back to summary |
---|---|
pack-priv static void put(ScopedValue<?> key, Object value) |
secondaryIndex | back to summary |
---|---|
pack-priv static int secondaryIndex(ScopedValue<?> key) |
secondarySlot | back to summary |
---|---|
private static int secondarySlot(ScopedValue<?> key) |
secondarySlot | back to summary |
---|---|
pack-priv static int secondarySlot(int hash) |
setKey | back to summary |
---|---|
private static void setKey(Object[] objs, int n, Object key) |
setKeyAndObjectAt | back to summary |
---|---|
private static void setKeyAndObjectAt(int n, Object key, Object value) |
setKeyAndObjectAt | back to summary |
---|---|
private static void setKeyAndObjectAt(Object[] cache, int n, Object key, Object value) |
Preview
Third Preview of Scoped Values (JEP 481).
Programs can only use CallableOp
when preview features are enabled.
Preview features may be removed in a future release,
or upgraded to permanent features of the Java platform.
Modifier and Type | Method and Description |
---|---|
public T |
call | back to summary |
---|---|
public T call() throws CallableOp:X Executes this operation.
|
Preview
Third Preview of Scoped Values (JEP 481).
Programs can only use Carrier
when preview features are enabled.
Preview features may be removed in a future release,
or upgraded to permanent features of the Java platform.
A Carrier
is used to accumulate mappings so that an operation (a Runnable
or CallableOp
) can be executed with all scoped values in the
mapping bound to values. The following example runs an operation with k1
bound (or rebound) to v1
, and k2
bound (or rebound) to v2
.
A Carrier
is immutable and thread-safe. The where
method returns a new Carrier
object,
it does not mutate an existing mapping.
Unless otherwise specified, passing a null
argument to a method in
this class will cause a NullPointerException
to be thrown.
Modifier and Type | Field and Description |
---|---|
pack-priv final int | |
pack-priv final ScopedValue | |
pack-priv final ScopedValue. | |
pack-priv final Object |
Access | Constructor and Description |
---|---|
pack-priv |
Modifier and Type | Method and Description |
---|---|
public < the type of the result of the operation R, type of the exception thrown by the operation X extends Throwable> R | Returns: the resultthe operation to run opCalls a value-returning operation with each scoped value in this mapping bound to its value in the current thread. |
pack-priv Object | |
public < the type of the value T> T | Returns: the valuethe key)ScopedValue keyReturns the value of a |
pack-priv ScopedValue | |
pack-priv static <T> ScopedValue. | |
public void | |
private <R, X extends Throwable> R | runWith(ScopedValue.
Execute the action with a set of ScopedValue bindings. |
private void | runWith(ScopedValue.
Execute the action with a set of |
private static <T> ScopedValue. | where(ScopedValue<T> key, T value, ScopedValue.
Add a binding to this map, returning a new Carrier instance. |
public < the type of the value T> ScopedValue. | Returns: a newCarrier with the mappings from this carrier plus the new mappingthe key, T ScopedValue keythe value, can be value)null Returns a new |
bitmask | back to summary |
---|---|
pack-priv final int bitmask |
key | back to summary |
---|---|
pack-priv final ScopedValue<?> key |
prev | back to summary |
---|---|
pack-priv final ScopedValue. |
value | back to summary |
---|---|
pack-priv final Object value |
Carrier | back to summary |
---|---|
pack-priv Carrier(ScopedValue<?> key, Object value, ScopedValue. |
call | back to summary |
---|---|
public <R, X extends Throwable> R call(ScopedValue. Calls a value-returning operation with each scoped value in this mapping bound
to its value in the current thread.
When the operation completes (normally or with an exception), each scoped value
in the mapping will revert to being unbound, or revert to its previous value
when previously bound, in the current thread. If Scoped values are intended to be used in a structured manner. If code
invoked directly or indirectly by the operation creates a
|
get | back to summary |
---|---|
pack-priv Object get() |
get | back to summary |
---|---|
public <T> T get(ScopedValue<T> key) Returns the value of a
|
getKey | back to summary |
---|---|
pack-priv ScopedValue |
of | back to summary |
---|---|
pack-priv static <T> ScopedValue. |
run | back to summary |
---|---|
public void run(Runnable op) Runs an operation with each scoped value in this mapping bound to its value
in the current thread.
When the operation completes (normally or with an exception), each scoped value
in the mapping will revert to being unbound, or revert to its previous value
when previously bound, in the current thread. If Scoped values are intended to be used in a structured manner. If code
invoked directly or indirectly by the operation creates a
|
runWith | back to summary |
---|---|
private <R, X extends Throwable> R runWith(ScopedValue. Execute the action with a set of ScopedValue bindings. The VM recognizes this method as special, so any changes to the name or signature require corresponding changes in JVM_FindScopedValueBindings().
|
runWith | back to summary |
---|---|
private void runWith(ScopedValue. Execute the action with a set of
|
where | back to summary |
---|---|
private static <T> ScopedValue. Add a binding to this map, returning a new Carrier instance. |
where | back to summary |
---|---|
public <T> ScopedValue. Returns a new
|
ScopedValue
to values.
Unless otherwise specified, passing a null
argument to a constructor
or method in this class will cause a NullPointerException
to be thrown.
Modifier and Type | Field and Description |
---|---|
pack-priv final ScopedValue. | |
pack-priv final int | |
pack-priv static final ScopedValue. | |
private static final Object | |
pack-priv final ScopedValue. |
Access | Constructor and Description |
---|---|
pack-priv | |
protected |
Modifier and Type | Method and Description |
---|---|
pack-priv Object |
bindings | back to summary |
---|---|
pack-priv final ScopedValue. |
bitmask | back to summary |
---|---|
pack-priv final int bitmask |
EMPTY_SNAPSHOT | back to summary |
---|---|
pack-priv static final ScopedValue. |
NIL | back to summary |
---|---|
private static final Object NIL |
prev | back to summary |
---|---|
pack-priv final ScopedValue. |
Snapshot | back to summary |
---|---|
pack-priv Snapshot(ScopedValue. |
Snapshot | back to summary |
---|---|
protected Snapshot() |
find | back to summary |
---|---|
pack-priv Object find(ScopedValue<?> key) |