Module java.base
Package java.lang

Class ClassValue<T>

java.lang.Object
java.lang.ClassValue<T>
类型参数:
T - 派生值的类型

public abstract class ClassValue<T> extends Object
惰性地将计算值与(可能)每种类型关联起来。例如,如果动态语言需要为在消息发送调用站点遇到的每个类构造消息分发表,它可以使用 ClassValue 来缓存执行消息发送所需的信息,以便为每个遇到的类快速执行消息发送。
自版本:
1.7
  • Constructor Details

    • ClassValue

      protected ClassValue()
      唯一构造函数。(通常由子类构造函数隐式调用。)
  • Method Details

    • computeValue

      protected abstract T computeValue(Class<?> type)
      为此 ClassValue 计算给定类的派生值。

      此方法将在访问具有 get 方法的第一个线程中调用。

      通常,此方法最多每个类调用一次,但如果已调用 remove,则可能会再次调用。

      如果此方法引发异常,则相应的对 get 的调用将以该异常异常终止,并且不会记录任何类值。

      参数:
      type - 必须计算其类值的类型
      返回:
      与给定类或接口关联的新计算值
      另请参阅:
    • get

      public T get(Class<?> type)
      返回给定类的值。如果尚未计算任何值,则通过调用 computeValue 方法获取它。

      在类上实际安装值是原子性执行的。在那一点上,如果有多个竞争线程计算值,将选择一个值,并返回给所有竞争线程。

      type 参数通常是一个类,但它可以是任何类型,例如接口、原始类型(如 int.class)或 void.class

      在没有 remove 调用的情况下,类值具有简单的状态图:未初始化和已初始化。当进行 remove 调用时,值观察的规则更加复杂。有关更多信息,请参阅 remove 的文档。

      参数:
      type - 必须计算或检索其类值的类型
      返回:
      与给定类或接口关联的当前值
      抛出:
      NullPointerException - 如果参数为 null
      另请参阅:
    • remove

      public void remove(Class<?> type)
      移除给定类的关联值。如果随后为相同类 读取 此值,则将通过调用其 computeValue 方法重新初始化其值。这可能导致为给定类调用 computeValue 方法的额外调用。

      为了解释 getremove 调用之间的交互,我们必须对类值的状态转换进行建模,以考虑未初始化和已初始化状态之间的交替。为此,从零开始顺序编号这些状态,并注意未初始化(或已移除)状态用偶数编号,而已初始化(或重新初始化)状态用奇数编号。

      当线程 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 的先前操作多次
      • 此外,相关计算值随时间变化: V1V2、...
      • ...同时,T 醒来并尝试安装 V0这必须失败
      我们可以假设在上述情景中,CV.computeValue 使用锁来正确观察计算 V1 等的时间相关状态。这并不能消除过时值的威胁,因为在 T 中的 computeValue 返回和新值安装之间存在时间窗口。在此期间,用户同步是不可能的。
      参数:
      type - 必须移除其类值的类型
      抛出:
      NullPointerException - 如果参数为 null