Max
Max

Reputation: 93

Are java.lang.Class methods thread safe?

Under IBM JVM we have faced an issue when multiple threads are trying to call Class.getAnnotation at the same time on different objects (but with the same annotation). Threads are starting to deadlock waiting on a monitor inside a Hashtable, which is used as a cache for annotations in IBM JVM. The weirdest thing is that the thread that is holding this monitor is put into 'waiting on condition' state right inside Hashtable.get, making all other threads to wait indefinitely.

The support from IBM stated, that implementation of Class.getAnnotation is not thread safe.

Comparing to other JVM implementations (for example, OpenJDK) we see that they implement Class methods in thread safe manner. IBM JVM is a closed source JVM, they do publish some source code together with their JVM, but it's not enough to make a clear judgment whenever their implementation of Class is thread safe or not.

The Class documentation doesn't clearly state whenever its methods are thread safe or not. So is it a safe assumption to treat Class methods (getAnnotation in particular) as a thread safe or we must use sync blocks in multi threaded environment?

How do popular frameworks (ex. Hibernate) are mitigating this problem? We haven't found any usage of synchronization in Hibernate code that was using getAnnotation method.

Upvotes: 9

Views: 1024

Answers (2)

Holger
Holger

Reputation: 298153

Well, there is no specified behavior, so normally the correct way to deal with it would be to say “if no behavior is specified, assume no safety guarantees”.

But…

The problem here is that if these methods are not thread-safe, the specification lacks a documentation of how to achieve thread-safety correctly here. Recall that instances of java.lang.Class are visible across all threads of the entire application or even within multiple applications if your JVM hosts multiple apps/applets/servlets/beans/etc.

So unlike classes you instantiate for your own use where you can control access to these instances, you can’t preclude other threads from accessing the same methods of a particular java.lang.Class instance. So even if we engage with the very awkward concept of relying on some kind of convention for accessing such a global resource (e.g. like saying “the caller has to do synchronized(x.class)”), the problem here is, even bigger, that no such convention exists (well, or isn’t documented which comes down to the same).

So in this special case, where no caller’s responsibility is documented and can’t be established without such a documentation, IBM is in charge of telling how they think, programmers should use these methods correctly when they are implemented in a non-thread-safe manner.


There is an alternative interpretation I want to add: all information, java.lang.Class offers, is of a static constant nature. This class reflects what has been invariably compiled into the class. And it has no methods to alter any state. So maybe there’s no additional thread-safety documentation as all information is to be considered immutable and hence naturally thread-safe.

Rather, the fact that under the hood some information is loaded on demand is the undocumented implementation detail that the programmer does not need to be aware of. So if JRE developers decide to implement lazy creation for efficiency they must maintain the like-immutable behavior, read thread safety.

Upvotes: 4

Your problem might be related to bug fixed in version 8 of Oracle Java.

One thread calls isAnnotationPresent on an annotated class where the annotation is not yet initialised for its defining classloader. This will result in a call on AnnotationType.getInstance, locking the class object for sun.reflect.annotation.AnnotationType. getInstance will result in a Class.initAnnotationsIfNecessary for that annotation, trying to acquire a lock on the class object of that annotation.

In the meanwhile, another thread has requested Class.getAnnotations for that annotation(!). Since getAnnotations locks the class object it was requested on, the first thread can't lock it when it runs into Class.initAnnotationsIfNecessary for that annotation. But the thread holding the lock will try to acquire the lock for the class object of sun.reflect.annotation.AnnotationType in AnnotationType.getInstance which is hold by the first thread, thus resulting in the deadlock.

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

Upvotes: 6

Related Questions