java.lang.Object
java.lang.ClassValue<T>
- 类型参数:
-
T
- 派生值的类型
惰性地将计算值与(可能)每种类型关联起来。例如,如果动态语言需要为在消息发送调用站点遇到的每个类构造消息分发表,它可以使用
ClassValue
来缓存执行消息发送所需的信息,以便为每个遇到的类快速执行消息发送。
- 自版本:
- 1.7
-
Constructor Summary
-
Method Summary
-
Constructor Details
-
ClassValue
protected ClassValue()唯一构造函数。(通常由子类构造函数隐式调用。)
-
-
Method Details
-
computeValue
为此ClassValue
计算给定类的派生值。此方法将在访问具有
get
方法的第一个线程中调用。通常,此方法最多每个类调用一次,但如果已调用
remove
,则可能会再次调用。如果此方法引发异常,则相应的对
get
的调用将以该异常异常终止,并且不会记录任何类值。- 参数:
-
type
- 必须计算其类值的类型 - 返回:
- 与给定类或接口关联的新计算值
- 另请参阅:
-
get
返回给定类的值。如果尚未计算任何值,则通过调用computeValue
方法获取它。在类上实际安装值是原子性执行的。在那一点上,如果有多个竞争线程计算值,将选择一个值,并返回给所有竞争线程。
type
参数通常是一个类,但它可以是任何类型,例如接口、原始类型(如int.class
)或void.class
。在没有
remove
调用的情况下,类值具有简单的状态图:未初始化和已初始化。当进行remove
调用时,值观察的规则更加复杂。有关更多信息,请参阅remove
的文档。- 参数:
-
type
- 必须计算或检索其类值的类型 - 返回:
- 与给定类或接口关联的当前值
- 抛出:
-
NullPointerException
- 如果参数为 null - 另请参阅:
-
remove
移除给定类的关联值。如果随后为相同类 读取 此值,则将通过调用其computeValue
方法重新初始化其值。这可能导致为给定类调用computeValue
方法的额外调用。为了解释
get
和remove
调用之间的交互,我们必须对类值的状态转换进行建模,以考虑未初始化和已初始化状态之间的交替。为此,从零开始顺序编号这些状态,并注意未初始化(或已移除)状态用偶数编号,而已初始化(或重新初始化)状态用奇数编号。当线程
T
移除状态为2N
的类值时,不会发生任何事情,因为类值已经未初始化。否则,状态会原子地提升到2N+1
。当线程
T
查询状态为2N
的类值时,线程首先尝试通过调用computeValue
并安装结果值来将类值初始化为状态2N+1
。当
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
等的时间相关状态。这并不能消除过时值的威胁,因为在T
中的computeValue
返回和新值安装之间存在时间窗口。在此期间,用户同步是不可能的。- 参数:
-
type
- 必须移除其类值的类型 - 抛出:
-
NullPointerException
- 如果参数为 null
-