懒惰地将计算值与(潜在的)每种类型关联起来。例如,如果一个动态语言需要为在消息发送调用站点遇到的每个类构造一个消息分派表,它可以使用一个ClassValue来缓存为快速执行消息发送所需的信息,对于遇到的每个类。
主要 方法:
protected abstract T computeValue(Class> type)
计算此给定类的派生值ClassValue。
将在使用该get方法访问值的第一个线程内调用此方法。
通常,每个类最多只能调用一次此方法,但是如果已经调用,则可以再次调用此方法 remove。
如果此方法引发异常,则对该异常的相应调用get 将异常终止,并且不会记录任何类值。
参数:
type -必须计算其类值的类型
返回值:
ClassValue对于给定的类或接口,与此this关联的新计算值
也可以看看:
get(java.lang.Class>), remove(java.lang.Class>)
public T get(Class> type)
返回给定类的值。如果尚未计算任何值,则通过调用该computeValue方法获得该值。
该值在类上的实际安装是原子执行的。在这一点上,如果多个赛车线程具有计算值,则选择一个,然后返回到所有赛车线程。
的type参数典型地是一类,但它可以是任何类型,例如一个接口,一个原语类型(像int.class),或void.class。
在没有remove调用的情况下,类值具有简单的状态图:未初始化和初始化。当remove调用制成,价值观察的规则更为复杂。有关remove更多信息,请参见文档。
参数:
type -必须计算或检索其类值的类型
返回值:
ClassValue对于给定的类或接口,与此this关联的当前值
抛出:
NullPointerException -如果参数为null
也可以看看:
remove(java.lang.Class>), computeValue(java.lang.Class>)
public void remove(Class> type)
删除给定类的关联值。如果随后为同一类读取此值,则将通过调用其computeValue方法来重新初始化其值。这可能会导致computeValue针对给定类的方法的其他调用 。
为了解释get和remove调用之间的交互,我们必须对类值的状态转换建模,以考虑未初始化状态和已初始化状态之间的交替。为此,请从零开始依次编号这些状态,并注意未初始化(或删除)的状态用偶数编号,而已初始化(或重新初始化)的状态则为奇数。
当线程T删除state中的类值2N时,由于该类值已被初始化,因此不会发生任何事情。否则,该状态会原子地推进到2N+1。
当线程T查询state中的类值时2N,该线程首先尝试2N+1 通过调用computeValue并安装结果值来将类值初始化为state 。
当T尝试安装新计算出的值,如果状态依然在2N,类值将与计算值初始化,它推进到状态2N+1。
否则,无论新状态是偶数还是奇数, T都将丢弃新计算的值并重试该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在in 返回T和安装之间存在时间窗口新价值。在此期间,无法进行用户同步。
参数:
type -必须删除其类值的类型
抛出:
NullPointerException -如果参数为null