20

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;
Till Helge
  • 9,253
  • 2
  • 40
  • 56
lies
  • 361
  • 2
  • 4
  • 9
  • 2
    If you really want to achieve a singleton design pattern, then follow [enum type](http://en.wikipedia.org/wiki/Singleton_pattern#The_Enum_way) in Java - "*a single-element enum type is the best way to implement a singleton*" – Lion Mar 01 '13 at 11:46
  • 8
    the enum pattern would not prevent the presence of one instance per class loader, would it? – keuleJ Mar 01 '13 at 11:49
  • Unfortunately enums can be by different classloaders, which as you can imagine can cause all sort of problems. – Sean Landsman Mar 01 '13 at 13:49

2 Answers2

31

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));
}
Sean Landsman
  • 7,033
  • 2
  • 29
  • 33
  • 1
    Nice answer! It would be even better if you included a code example of the solution here, with credit to the linked article. – Paul Bellora Mar 01 '13 at 13:21
  • 3
    How does this code actually help? How will this be invoked to make sure that the singleton remains a singleton across ClassLoaders? – Ian McLaird Feb 10 '14 at 17:25
  • There is one more very good web resource to understand this issue. https://weblogs.java.net/blog/2005/08/24/how-single-can-your-singleton-instance-be – Ankit Kumar Jun 30 '14 at 09:39
  • 3
    Are you sure that Sneha is the author of that post (or at least part about class loaders)? From what I see linked post was published `8 JANUARY 2009` but it looks the same as part of quite old [article from javaworld](http://www.javaworld.com/article/2073352/core-java/simply-singleton.html) published `Apr 25, 2003`. Post on blog contains even sentence "*The preceding method can be used instead of `Class.forName()`*" which seems out of context because there is no `Class.forName()` method used anywhere earlier, while it makes sense in javaworld since Example 10 uses reflection. – Pshemo Nov 26 '14 at 19:30
  • @Pshemo Thanks for that - I've updated my answer to reflect the likely original source – Sean Landsman Nov 27 '14 at 11:34
  • 3
    Here is a Internet Archive Wayback Machine link for the defunct java.net blog URL provided by @AnkitKumar: https://web.archive.org/web/20051117213814/http://weblogs.java.net:80/blog/kirillcool/archive/2005/08/how_single_is_y.html. This author is Kirill Grouchnikov and the title is "How single can your singleton instance be?" – buzz3791 Nov 03 '17 at 16:05
  • where should i put this method, who will invoke it? – Narendra Jaggi May 07 '20 at 12:41
2

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.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • One bug in there... fixing – Ondra Žižka Nov 22 '17 at 23:59
  • It will be create a real singleton, but depending on your use case it won't work because you will get `Caused by: java.lang.ClassCastException: class somepackage.config.Config cannot be cast to class somepackage.config.Config` cause the same class on different classloaders is a different class. So depending on the use case people want one singleton per classloader as the jvm default. – deFreitas Feb 24 '23 at 23:03