Am I synchronizing on the same object instance in the following code? Can I do it safely?
Class Sync {}
Class A implements Runnable {
void run() {
synchronized(Sync.class) {
System.out.println("I am synchronized");
}
}
}
Class B implements Runnable {
void run() {
synchronized(Sync.class) {
System.out.println("I too am synchronized");
}
}
}
You have to make a distinction between Loading of a Class
and Resolving a Class
.
Class literals and the special method Class.forName(…)
are different than the method ClassLoader.loadClass(…)
. While a ClassLoader
might implement the latter in strange ways, e.g. by returning different instances on each call, the JVM will resolve each class exactly once per context and remember the result.
The important point is, if the resolution of a symbolic reference could be redirected, a static final
variable would not help either. If you have two instances of the same Class
in your JVM, two different version of their static final
fields would exist as well.
There is a one-by-one mapping between a Class
instance and the JVM per class data.
To cite a few official words:
JVMSpec § 5.3.2. Loading Using a User-defined Class Loader
… First, the Java Virtual Machine determines whether L has already been recorded as an initiating loader of a class or interface denoted by N. If so, this class or interface is C, and no class creation is necessary.
…
…
JVMSpec § 5.3.5. Deriving a Class from a class File Representation
The following steps are used to derive a Class object for the nonarray class or interface C denoted by N using loader L from a purported representation in class file format.
(1) First, the Java Virtual Machine determines whether it has already recorded that L is an initiating loader of a class or interface denoted by N. If so, this creation attempt is invalid and loading throws a LinkageError.
…
(5) The Java Virtual Machine marks C as having L as its defining class loader and records that L is an initiating loader of C (§5.3.4).
The important point as pointed out by § 5.3.5 is that each ClassLoader
can define at most one class per unique symbolic name. It might return different instances by delegating to different loaders but then they would be remembered as the defining loader. And the JVM will ask the defining loader when resolving references from a Class
to another Class
. Or skip asking it as § 5.3.2 states when a Class
for a given name and loader already exists.
According to JLS 8.4.3.6 synchronizing on Class.forName("name")
and, hence, .class
is the right thing to do and it will work - in the same class, at least. Moreover, all static synchronized
methods are synchronized on .class
. As it says in the JLS, the following are virtually the same:
class Test {
int count;
synchronized void bump() {
count++;
}
static int classCount;
static synchronized void classBump() {
classCount++;
}
}
class Test {
int count;
void bump() {
synchronized (this) { count++; }
}
static int classCount;
static void classBump() {
try {
synchronized (Class.forName("Test")) {
classCount++;
}
} catch (ClassNotFoundException e) {}
}
}
However, as JLS 12.2 states, it is not certain when .class
is called by different classes, since this call depends on the class loader:
Well-behaved class loaders maintain these properties:
Given the same name, a good class loader should always return the same class object.
If a class loader L1 delegates loading of a class C to another loader L2, then for any type T that occurs as the direct superclass or
a direct superinterface of C, or as the type of a field in C, or as
the type of a formal parameter of a method or constructor in C, or as
a return type of a method in C, L1 and L2 should return the same Class
object.
A malicious class loader could violate these properties. However, it could not undermine the security of the type system, because the Java Virtual Machine guards against this.
So if you are using a standard Java class loader - you should be fine. If you are using some custom class loaders, however, you should watch out for errors.
Only if they have the same classloader.
If you have a root-classloader and two child-class-loader the two child-class-loader (ie URLClassLoader) load the .class once per classloader, they will work async.
Load Sync
in the root-classloader will be save.