public abstract class ClassValue<T> extends ObjectLazily associate a computed value with (potentially) every type. For example, if a dynamic language needs to construct a message dispatch table for each class encountered at a message send call site, it can use a
ClassValueto cache information needed to perform the message send quickly, for each class encountered.
computeValueComputes the given class's derived value for this
This method will be invoked within the first thread that accesses the value with the
Normally, this method is invoked at most once per class, but it may be invoked again if there has been a call to
If this method throws an exception, the corresponding call to
getwill terminate abnormally with that exception, and no class value will be recorded.
getReturns the value for the given class. If no value has yet been computed, it is obtained by an invocation of the
The actual installation of the value on the class is performed atomically. At that point, if several racing threads have computed values, one is chosen, and returned to all the racing threads.
typeparameter is typically a class, but it may be any type, such as an interface, a primitive type (like
In the absence of
removecalls, a class value has a simple state diagram: uninitialized and initialized. When
removecalls are made, the rules for value observation are more complex. See the documentation for
removefor more information.
public void remove(Class<?> type)Removes the associated value for the given class. If this value is subsequently read for the same class, its value will be reinitialized by invoking its
computeValuemethod. This may result in an additional invocation of the
computeValuemethod for the given class.
In order to explain the interaction between
removecalls, we must model the state transitions of a class value to take into account the alternation between uninitialized and initialized states. To do this, number these states sequentially from zero, and note that uninitialized (or removed) states are numbered with even numbers, while initialized (or re-initialized) states have odd numbers.
When a thread
Tremoves a class value in state
2N, nothing happens, since the class value is already uninitialized. Otherwise, the state is advanced atomically to
When a thread
Tqueries a class value in state
2N, the thread first attempts to initialize the class value to state
computeValueand installing the resulting value.
Tattempts to install the newly computed value, if the state is still at
2N, the class value will be initialized with the computed value, advancing it to state
Otherwise, whether the new state is even or odd,
Twill discard the newly computed value and retry the
Discarding and retrying is an important proviso, since otherwise
Tcould potentially install a disastrously stale value. For example:
CV.get(C)and sees state
Tquickly computes a time-dependent value
V0and gets ready to install it
Tis hit by an unlucky paging or scheduling event, and goes to sleep for a long time
CV.get(C)and sees state
T2quickly computes a similar time-dependent value
V1and installs it on
T2(or a third thread) then calls
- the previous actions of
T2are repeated several times
- also, the relevant computed values change over time:
Twakes up and attempts to install
V0; this must fail
CV.computeValueuses locks to properly observe the time-dependent states as it computes
V1, etc. This does not remove the threat of a stale value, since there is a window of time between the return of
Tand the installation of the new value. No user synchronization is possible during this time.
type- the type whose class value must be removed
NullPointerException- if the argument is null