java.lang.Class方法是否安全?

Max*_*Max 9 java concurrency multithreading jvm

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

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

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

只要其方法是线程安全的,Class文档就不会明确说明.那么将Class方法(特别是getAnnotation)视为线程安全或者我们必须在多线程环境中使用同步块是一个安全的假设吗?

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

Dam*_*ash 6

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

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

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

JDK-7122142:(ann)isAnnotationPresent和getAnnotations之间的竞争条件

  • 由于问题与IBM VM有关,我不明白为什么Oracle虚拟机中的错误应该与此相关? (3认同)

Hol*_*ger 4

嗯,没有指定的行为,所以通常处理它的正确方法是说 \xe2\x80\x9c 如果没有指定行为,则假设没有安全保证\xe2\x80\x9d。

\n\n

但是\xe2\x80\xa6

\n\n

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

\n\n

因此,与您实例化供自己使用的类不同,您可以控制对这些实例的访问,\xe2\x80\x99 不能阻止其他线程访问特定实例的相同方法java.lang.Class。因此,即使我们采用依赖某种约定来访问此类全局资源的非常尴尬的概念(例如,调用者必须执行 \xe2\x80\x9c 操作\xe2\x80\x9d),这里的问题是,更大的是,不存在这样的约定(好吧,或者没有\xe2\x80\x99t记录,归根结底是相同的)。synchronized(x.class)

\n\n

因此,在这种特殊情况下,没有记录调用者\xe2\x80\x99s的责任,并且在没有这样的文档的情况下可以建立\xe2\x80\x99t,IBM负责告诉他们如何思考,程序员应该在以下情况下正确使用这些方法:它们以非线程安全的方式实现。

\n\n
\n\n

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

\n\n

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

\n