Singleton class with several different classloaders
E.g I have class Singleton
with static field instance
:
public class Singleton {
private static Singleton instance;
// other code, construct, getters, no matter
}
I can load this class twice with two different classloaders. How could I avoid it? It is unsafe and dangerous.
Also, if I set instance to null, would it set to null for both classes?
Singleton singleton = Singleton.getInstance();
singleton = null;
If you want a true Singleton
across classloaders, then you need a common parent to load the class in question, or you need to specify the classloader yourself.
Update: From the comment from @Pshemo below a fair bit of the content in the blog below might come directly from a JavaWorld Article. I've left the blog entry in as it may still help someone, but its worth knowing where the content originally came from.
Original: There is a blog entry that gives you a way to do this" (although I havent tried it!), and it looks fairly reasonable
As requested below here a code snippet from my link above - I do suggest you visit the blog though for the full context:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
This is a hack misusing the fact that Properties
extends Map
, an old unfortunate design decision.
public final class JvmWideSingleton
{
private static final JvmWideSingleton INSTANCE;
static {
// There should be just one system class loader object in the whole JVM.
synchronized(ClassLoader.getSystemClassLoader()) {
Properties sysProps = System.getProperties();
// The key is a String, because the .class object would be different across classloaders.
JvmWideSingleton singleton = (JvmWideSingleton) sysProps.get(JvmWideSingleton.class.getName());
// Some other class loader loaded JvmWideSingleton earlier.
if (singleton != null) {
INSTANCE = singleton;
}
else {
// Otherwise this classloader is the first one, let's create a singleton.
// Make sure not to do any locking within this.
INSTANCE = new JvmWideSingleton();
System.getProperties().put(JvmWideSingleton.class.getName(), INSTANCE);
}
}
}
public static JvmWideSingleton getSingleton() {
return INSTANCE;
}
}
This could be made parametrized, but then the initialization would be lazy and go to getSingleton()
.
Properties
is Hashtable
-based, so it is thread safe (as per the documentation). So one could use props.computeIfAbsent()
. But I like it this way more.
Also read here: Scope of the Java System Properties
I just wrote it and there is a chance there's something I overlooked that would prevent this from working.