- Type Parameters:
T- the extent local's type
An extent-local variable is bound, meaning it gets a value, when invoking an
operation with the Carrier.run or Carrier.call methods. Carrier instances are
created by the static method where(ExtentLocal, Object). The operations
executed by the run and call methods use the get() method to
read the value of a bound extent local. An extent-local variable reverts to being
unbound (or its previous value) when the operation completes.
An ExtentLocal object will typically be declared in a private
static final field so that it can only be accessed by code in that class (or other
classes within its nest).
ExtentLocal bindings are immutable: there is no "set" method.
There may be cases when an operation might need to use the same extent-local variable
to communicate a different value to the methods that it calls. The requirement is not
to change the original binding but to establish a new binding for nested calls. If an
extent local already has a value, then run or call methods may be
invoked to run another operation with a newly-bound value. Code executed by the
operation will read the new value of the extent local. The extent local reverts to its
previous value when the operation completes.
Sharing extent-local variables across threads
Extent-local variables can be shared across threads when used in conjunction withStructuredTaskScope. Creating a StructuredTaskScope captures the
current thread's extent-local bindings for inheritance by threads forked in the task scope. This means that a thread
may bind an extent-local variable and share its value in a structured concurrency
context. Threads forked in the task scope that read the extent-local variable will read
the value bound by the thread that created the task scope.
Unless otherwise specified, passing a null argument to a constructor
or method in this class will cause a NullPointerException to be thrown.
- API Note:
- The following example uses an extent local to make credentials available to callees.
private static final ExtentLocal<Credentials> CREDENTIALS = ExtentLocal.newInstance(); Credentials creds = ... ExtentLocal.where(CREDENTIALS, creds).run(() -> { ... Connection connection = connectDatabase(); ... }); ... Connection connectDatabase() { Credentials credentials = CREDENTIALS.get(); ... } - Implementation Note:
- Extent-local variables are designed to be used in fairly small
numbers.
get()initially performs a search through enclosing scopes to find an extent-local variable's innermost binding. It then caches the result of the search in a small thread-local cache. Subsequent invocations ofget()for that extent local will almost always be very fast. However, if a program has many extent-local variables that it uses cyclically, the cache hit rate will be low and performance will be poor. This design allows extent-local inheritance byStructuredTaskScopethreads to be very fast: in essence, no more than copying a pointer, and leaving an extent-local binding also requires little more than updating a pointer.Because the extent-local per-thread cache is small, you should try to minimize the number of bound extent-local variables in use. For example, if you need 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 extent-local variable to an instance of that record.
For this incubator release, we have provided some system properties to tune the performance of extent-local variables.
The system property
jdk.incubator.concurrent.ExtentLocal.cacheSizecontrols the size of the (per-thread) extent-local cache. This cache is crucial for the performance of extent-local variables. If it is too small, the runtime library will repeatedly need to scan for eachget(). If it is too large, memory will be unnecessarily consumed. The default extent-local cache size is 16 entries. It may be varied from 2 to 16 entries in size.ExtentLocal.cacheSizemust be an integer power of 2.For example, you could use
-Djdk.incubator.concurrent.ExtentLocal.cacheSize=8.The other system property is
jdk.preserveExtentLocalCache. This property determines whether the per-thread extent-local cache is preserved when a virtual thread is blocked. By default this property is set totrue, meaning that every virtual thread preserves its extent-local cache when blocked. LikeExtentLocal.cacheSize, this is a space versus speed trade-off: if you have a great many virtual threads that are blocked most of the time, setting this property tofalsemight result in a useful memory saving, but each virtual thread's extent-local cache would have to be regenerated after a blocking operation. - Since:
- 19
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classAn immutable map of extent-local variables to values. -
Method Summary
Modifier and TypeMethodDescriptionget()Returns the current thread's bound value for this extent-local variable.booleanisBound()Returnstrueif the extent local is bound to a value.static <T> ExtentLocal<T>Creates an extent-local variable to refer to a value of type T.Returns the value of the extent local if bound, otherwise returnsother.orElseThrow(Supplier<? extends X> exceptionSupplier) Returns the value of the extent local if bound, otherwise throws the exception produced by the exception supplying function.static <T> ExtentLocal.Carrierwhere(ExtentLocal<T> key, T value) Creates a binding for an extent-local variable.static <T> voidwhere(ExtentLocal<T> key, T value, Runnable op) Creates a binding for extent-local variable and runs an operation with thatExtentLocalbound to the value.static <T,U> U where(ExtentLocal<T> key, T value, Callable<U> op) Creates a binding for an extent-local variable and runs a value-returning operation with thatExtentLocalbound to the value.
-
Method Details
-
where
Creates a binding for an extent-local variable. TheCarriermay be used later to invoke aCallableorRunnableinstance. More bindings may be added to theCarrierby further calls to this method.- Type Parameters:
T- the type of the ExtentLocal- Parameters:
key- the ExtentLocal to bindvalue- the value to bind it to, can benull- Returns:
- A Carrier instance that contains one binding, that of key and value
-
where
Creates a binding for an extent-local variable and runs a value-returning operation with thatExtentLocalbound to the value.- Type Parameters:
T- the type of the ExtentLocalU- the type of the Result- Parameters:
key- the ExtentLocal to bindvalue- the value to bind it to, can benullop- the operation to call- Returns:
- the result
- Throws:
Exception- if the operation completes with an exception
-
where
Creates a binding for extent-local variable and runs an operation with thatExtentLocalbound to the value.- Type Parameters:
T- the type of the ExtentLocal- Parameters:
key- the ExtentLocal to bindvalue- the value to bind it to, can benullop- the operation to run
-
newInstance
Creates an extent-local variable to refer to a value of type T.- Type Parameters:
T- the type of the extent local's value.- Returns:
- an extent-local variable
-
get
Returns the current thread's bound value for this extent-local variable.- Returns:
- the value of the extent local
- Throws:
NoSuchElementException- if the extent local is not bound
-
isBound
public boolean isBound()Returnstrueif the extent local is bound to a value.- Returns:
trueif the extent local is bound to a value
-
orElse
Returns the value of the extent local if bound, otherwise returnsother.- Parameters:
other- the value to return if not bound, can benull- Returns:
- the value of the extent local if bound, otherwise
other
-
orElseThrow
Returns the value of the extent local if bound, otherwise throws the exception produced by the exception supplying function.- Type Parameters:
X- Type of the exception to be thrown- Parameters:
exceptionSupplier- the supplying function that produces the exception to throw- Returns:
- the value of the extent local if bound
- Throws:
X- prodouced by the exception suppying function if the extent local is unbound
-