【问题标题】:Are java.lang.Class methods thread safe?java.lang.Class 方法是线程安全的吗?
【发布时间】:2014-08-21 01:01:09
【问题描述】:

在 IBM JVM 下,当多个线程尝试在不同对象上同时调用 Class.getAnnotation(但使用相同的注解)时,我们遇到了一个问题。线程开始在 Hashtable 内的监视器上等待死锁,该监视器用作 IBM JVM 中注释的缓存。最奇怪的是,持有此监视器的线程在 Hashtable.get 中被置于“等待条件”状态,使所有其他线程无限期等待。

IBM 的支持声明 Class.getAnnotation 的实现不是线程安全的。

与其他 JVM 实现(例如 OpenJDK)相比,我们看到它们以线程安全的方式实现 Class 方法。 IBM JVM 是一个闭源 JVM,他们确实会和他们的 JVM 一起发布一些源代码,但是他们的 Class 实现是否是线程安全的,还不足以做出明确的判断。

Class 文档没有明确说明它的方法是否是线程安全的。那么将 Class 方法(特别是 getAnnotation)视为线程安全是安全的假设,还是我们必须在多线程环境中使用同步块?

流行的框架(例如 Hibernate)如何缓解这个问题?我们没有在使用 getAnnotation 方法的 Hibernate 代码中发现任何同步用法。

【问题讨论】:

  • 虽然是个好问题,但对于这个论坛来说可能过于自以为是。 so who is wrong here?
  • 您说的是 OpenJDK,而是 IBM JVM。您实际使用的是哪种实现?
  • 正如我的一位导师曾经对我说的:“代码不会说谎。”如果这就是您在源代码中看到的,那么这就是正在发生的事情。
  • @jarnbjo Oracle 的实现是事实上的标准。如果您想出售与 Oracle 有很大差异的 JDK 或 JRE,即使您的产品在技术上是 已发布标准的正确实现,那么您将自行承担风险。跨度>
  • 如果他们声称它不是线程安全的,为什么他们使用线程安全集合 Hashtable?这听起来像一个错误。没有明显的理由应该锁定不可变对象以使其线程安全。

标签: java multithreading concurrency jvm


【解决方案1】:

好吧,没有指定的行为,所以通常处理它的正确方法是说“如果没有指定行为,则假设没有安全保证”。

但是……

这里的问题是,如果这些方法不是线程安全的,那么规范就缺少关于如何在此处正确实现线程安全的文档。回想一下,java.lang.Class 的实例在整个应用程序的所有线程中都是可见的,如果您的 JVM 托管多个应用程序/小程序/servlets/beans/等,甚至在多个应用程序中也是可见的。

因此,与您为自己使用而实例化的类不同,您可以控制对这些实例的访问,您不能阻止其他线程访问特定 java.lang.Class 实例的相同方法。因此,即使我们使用非常尴尬的概念,即依赖某种约定来访问这样的全局资源(例如,说“调用者必须做synchronized(x.class) ”),这里的问题甚至更大,不存在这样的约定(好吧,或者没有记录,归结为相同)。

因此,在这种特殊情况下,没有记录调用者的责任并且没有这样的文档就无法确定,IBM 负责告诉他们如何思考,程序员应该在非-线程安全的方式。


我想补充另一种解释:java.lang.Class 提供的所有信息都具有静态常量性质。此类反映了始终编译到该类中的内容。它没有改变任何状态的方法。所以也许没有额外的线程安全文档,因为所有信息都被视为不可变,因此自然是线程安全的。

相反,在后台按需加载某些信息这一事实是程序员不需要了解的未记录的实现细节。因此,如果 JRE 开发人员决定实现延迟创建以提高效率,他们必须保持类似不可变的行为,阅读线程安全。

【讨论】:

    【解决方案2】:

    您的问题可能与 Oracle Java 版本 8 中修复的错误有关。

    一个线程在一个带注释的类上调用 isAnnotationPresent,其中 注释尚未为其定义的类加载器初始化。这 将导致调用 AnnotationType.getInstance,锁定类 sun.reflect.annotation.AnnotationType 的对象。 getInstance 将 导致该注释的 Class.initAnnotationsIfNecessary , 试图获得对该注释的类对象的锁定。

    与此同时,另一个线程请求了 Class.getAnnotations 对于那个注释(!)。由于 getAnnotations 锁定了它的类对象 被请求,第一个线程在遇到时无法锁定它 该注释的 Class.initAnnotationsIfNecessary。但是线程 持有锁会尝试获取类对象的锁 Sun.reflect.annotation.AnnotationType 在 AnnotationType.getInstance 由第一个线程持有,从而导致死锁。

    JDK-7122142 : (ann) Race condition between isAnnotationPresent and getAnnotations

    【讨论】:

    • 这不完全是我们的情况——在我们的例子中所有类都已经加载(我们的代码在运行几个小时后挂起),而在错误堆栈跟踪中,JVM 第一次尝试加载注释类和死锁。
    • 在这种情况下,您应该尝试联系 IBM 支持团队或使用 Health Center 来诊断问题所在。
    • 我们确实联系了 IBM 支持,他们说 class.getAnnotation 在 IBM JVM 中不是线程安全的。这就是我提出这个问题的原因 - 每当我们可以预期 Class 方法是否是线程安全的时候得到意见,因为在其他 JVM 中它们是线程安全的。
    • 这个问题应该发布在一些 IBM 用户组或错误跟踪上。堆栈不是讨论的最佳场所,因为这是更多的问答。
    • 既然问题与IBM VM有关,我不明白为什么Oracle的VM中的错误应该与这里有关?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    相关资源
    最近更新 更多