* This method will be invoked within the first thread that accesses * the value with the {@link #get get} method. *
* Normally, this method is invoked at most once per class, * but it may be invoked again if there has been a call to * {@link #remove remove}. *
* If this method throws an exception, the corresponding call to {@code get} * will terminate abnormally with that exception, and no class value will be recorded. * * @param type the type whose class value must be computed * @return the newly computed value associated with this {@code ClassValue}, for the given class or interface * @see #get * @see #remove */ protected abstract T computeValue(Class type); /** * Returns the value for the given class. * If no value has yet been computed, it is obtained by * an invocation of the {@link #computeValue computeValue} method. *
* 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. *
* The {@code type} parameter is typically a class, but it may be any type, * such as an interface, a primitive type (like {@code int.class}), or {@code void.class}. *
* In the absence of {@code remove} calls, a class value has a simple
* state diagram: uninitialized and initialized.
* When {@code remove} calls are made,
* the rules for value observation are more complex.
* See the documentation for {@link #remove remove} for more information.
*
* @param type the type whose class value must be computed or retrieved
* @return the current value associated with this {@code ClassValue}, for the given class or interface
* @throws NullPointerException if the argument is null
* @see #remove
* @see #computeValue
*/
public T get(Class type) {
// non-racing this.hashCodeForCache : final int
Entry[] cache;
Entry
* In order to explain the interaction between {@code get} and {@code remove} calls, * 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 {@code T} removes a class value in state {@code 2N}, * nothing happens, since the class value is already uninitialized. * Otherwise, the state is advanced atomically to {@code 2N+1}. *
* When a thread {@code T} queries a class value in state {@code 2N}, * the thread first attempts to initialize the class value to state {@code 2N+1} * by invoking {@code computeValue} and installing the resulting value. *
* When {@code T} attempts to install the newly computed value, * if the state is still at {@code 2N}, the class value will be initialized * with the computed value, advancing it to state {@code 2N+1}. *
* Otherwise, whether the new state is even or odd, * {@code T} will discard the newly computed value * and retry the {@code get} operation. *
* Discarding and retrying is an important proviso, * since otherwise {@code T} could potentially install * a disastrously stale value. For example: *
* All user-visible state changes on the ClassValue take place under * a lock inside the synchronized methods of ClassValueMap. * Readers (of ClassValue.get) are notified of such state changes * when this.version is bumped to a new token. * This variable must be volatile so that an unsynchronized reader * will receive the notification without delay. *
* If version were not volatile, one thread T1 could persistently hold onto * a stale value this.value == V1, while while another thread T2 advances * (under a lock) to this.value == V2. This will typically be harmless, * but if T1 and T2 interact causally via some other channel, such that * T1's further actions are constrained (in the JMM) to happen after * the V2 event, then T1's observation of V1 will be an error. *
* The practical effect of making this.version be volatile is that it cannot
* be hoisted out of a loop (by an optimizing JIT) or otherwise cached.
* Some machines may also require a barrier instruction to execute
* before this.version.
*/
private volatile Version