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

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

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

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

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

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

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

但…

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

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

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


我想添加一个替代解释:所有信息, java.lang.Class提供,具有静态常量性质。 这个类反映了总是被编译到课堂中的东西。 它没有改变任何状态的方法。 所以也许没有额外的线程安全文档,因为所有信息都被认为是不可变的 ,因此自然是线程安全的。

相反,根据需要加载一些信息的事实是程序员不需要知道的未记录的实现细节。 因此,如果JRE开发人员决定实现延迟创建以提高效率,那么他们必须维护类似不可变的行为,阅读线程安全性。

您的问题可能与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之间的竞争条件