Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
like image 960
lies Avatar asked Mar 01 '13 11:03

lies


2 Answers

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));
}
like image 130
Sean Landsman Avatar answered Nov 16 '22 01:11

Sean Landsman


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.

like image 44
Ondra Žižka Avatar answered Nov 16 '22 01:11

Ondra Žižka