- java.lang.Object
-
- java.lang.ClassValue<T>
-
public abstract class ClassValue<T> extends Object
将计算的值与(潜在地)每种类型相关联。 例如,如果动态语言需要为在消息发送呼叫站点遇到的每个类构造消息分派表,则可以使用ClassValue
来缓存为每个遇到的类快速发送消息所需的信息。- 从以下版本开始:
- 1.7
-
-
方法详细信息
-
computeValue
protected abstract T computeValue(Class<?> type)
计算给定类的派生值为这个ClassValue
。该方法将在使用
get
方法访问该值的第一个线程中调用。通常,每个类最多调用一次此方法,但是如果已经调用了
remove
,则可以再次调用此方法。如果此方法引发异常,则相应调用
get
会异常终止该异常,并且不会记录类值。- 参数
-
type
- 必须计算其类值的类型 - 结果
-
与该
ClassValue
相关联的新计算值,用于给定的类或接口 - 另请参见:
-
get(java.lang.Class<?>)
,remove(java.lang.Class<?>)
-
get
public T get(Class<?> type)
返回给定类的值。 如果尚未计算值,则通过调用computeValue
方法获得。类的实际安装是以原子方式执行的。 在这一点上,如果几个赛车线程已经计算出值,则选择一个,并返回所有赛车线程。
type
参数通常是一个类,但它可以是任何类型,例如接口,基本类型(如int.class
)或void.class
。在没有
remove
调用的情况下,类值有一个简单的状态图:未初始化和初始化。 当进行remove
调用时,值观察的规则更为复杂。 有关详细信息,请参阅remove
的文档。- 参数
-
type
- 必须计算或检索类值的类型 - 结果
-
与此
ClassValue
相关联的当前值,针对给定的类或接口 - 异常
-
NullPointerException
- 如果参数为空 - 另请参见:
-
remove(java.lang.Class<?>)
,computeValue(java.lang.Class<?>)
-
remove
public void remove(Class<?> type)
删除给定类的关联值。 如果同一类的值为read ,则其值将通过调用其computeValue
方法重新初始化。 这可能导致额外调用给定类的computeValue
方法。为了解释
get
和remove
调用之间的交互,我们必须对类值的状态转换建模,以考虑未初始化和初始化状态之间的交替。 为了做到这一点,这些状态从零开始顺序排列,并且注意到未初始化(或去除)状态用偶数编号,而初始化(或重新初始化)状态具有奇数。当一个线程
T
消除状态类值2N
,什么都不会发生,因为类值已经初始化。 否则,国家原子级2N+1
。当线程
T
查询状态2N
的类值时,线程首先尝试通过调用computeValue
并安装结果值将类值初始化为状态2N+1
。当
T
尝试安装新计算的值时,如果状态仍处于2N
,则类值将使用计算值进行初始化,并将其2N+1
送到状态2N+1
。否则,新状态是偶数还是奇数,则
T
将丢弃新计算值get
试get
操作。丢弃和重试的一个重要条件,否则
T
可能会安装一个灾难性的失效值。 例如:-
T
致电CV.get(C)
并看到状态2N
-
T
快速计算时间依赖值V0
并准备安装它 -
T
受到不幸的分页或调度事件的打击,并长时间睡觉 - ...同时,
T2
也叫CV.get(C)
,看到状态2N
-
T2
快速计算类似的时间依赖值V1
并将其安装在CV.get(C)
-
T2
(或第三个线程)然后调用CV.remove(C)
,撤消T2
的工作 - 以前的动作
T2
重复了几次 - 同时,相关的计算值随时间而变化:
V1
,V2
,... - ...同时,
T
醒来,试图安装V0
; 这必须失败
CV.computeValue
使用锁来适当地观察时间依赖状态,因为它计算V1
等。这并不能消除陈旧价值的威胁,因为在返回computeValue
之间有时间窗口T
和安装的新值。 在此期间无法进行用户同步。- 参数
-
type
- 必须删除其类值的类型 - 异常
-
NullPointerException
- if the argument is null
-
-
-