java.lang.Class方法是线程安全的吗?

java.lang.Class方法是线程安全的吗?,java,multithreading,concurrency,jvm,Java,Multithreading,Concurrency,Jvm,在IBM JVM下,当多个线程试图在不同对象上同时调用Class.getAnnotation(但使用相同的注释)时,我们遇到了一个问题。线程开始死锁,等待哈希表中的监视器,哈希表用作IBMJVM中注释的缓存。最奇怪的是,持有此监视器的线程在Hashtable.get中被置于“waiting on condition”状态,使所有其他线程无限期地等待 IBM的支持指出,Class.getAnnotation的实现不是线程安全的 与其他JVM实现(例如OpenJDK)相比,我们看到它们以线程安全的方

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

IBM的支持指出,Class.getAnnotation的实现不是线程安全的

与其他JVM实现(例如OpenJDK)相比,我们看到它们以线程安全的方式实现类方法。IBMJVM是一个封闭源代码的JVM,他们确实会将一些源代码与JVM一起发布,但这还不足以清楚地判断类的实现是否是线程安全的

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


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

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

一个线程在注释类上调用isAnnotationPresent,其中 注释尚未为其定义类加载器初始化。这 将导致调用AnnotationType.getInstance,从而锁定该类 sun.reflect.annotation.AnnotationType的对象。getInstance将 生成该注释所需的Class.InitAnnotations, 正在尝试获取该批注的类对象上的锁

同时,另一个线程请求Class.getAnnotations 对于该注释(!)。由于getAnnotations锁定了类对象,因此 已在上请求,第一个线程在运行到时无法锁定它 Class.initAnnotations是该批注所必需的。但是线 持有锁将尝试获取对象类的锁 AnnotationType.getInstance中的sun.reflect.annotation.AnnotationType 由第一个线程持有,从而导致死锁


嗯,没有规定的行为,所以通常正确的处理方法是说“如果没有规定行为,就没有安全保证”

但是

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

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

因此,在这种特殊情况下,如果没有记录调用方的责任,并且没有这样的文档就无法建立调用方的责任,IBM负责告诉他们如何思考,当以非线程安全的方式实现这些方法时,程序员应该正确地使用这些方法


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


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

这是一个好问题,但对于本论坛来说可能过于固执己见<代码>那么谁错了?你说的是OpenJDK,而不是IBM JVM。您实际使用的是哪种实现?正如我的一位导师曾经对我说的那样:“代码不会说谎。”如果您在源代码中看到的是这种情况,那么这就是正在发生的事情。@jarnbjo Oracle的实现是事实上的标准。如果您想销售与Oracle在某些方面存在重大差异的JDK或JRE,即使您的JDK或JRE在技术上是已发布标准的正确实现,您也要自行承担风险。如果他们声称它不是线程安全的,为什么他们要使用线程安全集合哈希表?这听起来像个虫子。没有明显的理由应该锁定不可变对象以使其线程安全。这不完全是我们的情况-在我们的情况下,所有类都已加载(我们的代码在运行几个小时后挂起),而在bug stacktrace中,JVM第一次尝试加载注释类并出现死锁。在这种情况下,您应该尝试联系IBM支持团队或使用Health Center来诊断这一问题。我们确实联系了IBM支持人员,他们说class.getAnnotation在IBM JVM中不是线程安全的。这就是为什么我提出这个问题的原因——无论何时我们可以期望类方法是线程安全的或不是线程安全的,都要征求意见,因为在其他JVM中它们是线程安全的。这个问题应该发布在一些IBM用户组或bug跟踪上。书堆不是讨论的最佳场所