Search code examples
javamultithreadinggarbage-collectionsingletonweak-references

Is this weak singleton implementation in Java thread-safe (and GC-safe)?


I want to achieve the following results:

  • I want to have no more than one instance of the class around. When there are multiple references to the class, I want them all to refer to the same instance.
  • I don't want the instance around unless it is needed. Specifically, I want the garbage collector to be able to reclaim the instance when not in use, and recreate it the next time an instance is requested.
  • Multiple threads may request an instance, hence the operation must be thread-safe.

This is the code I came up with:

public final class MyClass {
    private static WeakReference<MyClass> instance;
    public static synchronized MyClass getInstance() {
        if ((instance == null) || (instance.get() == null)) {
            instance = new WeakReference<MyClass>(new MyClass());
        }
        // TODO what if GC strikes here?
        return instance.get();
    }
}

Design choices were:

  • Lazy initialization so the instance doesn’t get created unless needed
  • The class keeps a WeakReference to the instance so the instance can get garbage-collected when it is no longer used.
  • The getInstance() method is synchronized (to MyClass) so that it can only be executed by one thread at a time.

Questions:

  • Will this design behave as I expect it to and yield the desired result?
  • Do I have to worry about getInstance() getting interrupted by the garbage collector where the comment is (meaning the garbage collector would reclaim the instance I was just about to return)? If so, how can I work around it?

Solution

  • Hold a local copy of MyClass in a variable instead of just giving your only copy of the reference in to the constructor of WeakRefrence. This will prevent the GC from collecting instance between the new WeakReference<MyClass> call and the function returning.

    public final class MyClass {
        private static WeakReference<MyClass> instance;
        public static synchronized MyClass getInstance() {
            MyClass classInstance = null;
            if (instance != null) {
                classInstance = instance.get();
                if(classInstance != null)
                {
                    return classInstance;
                }
            }
    
            classInstance = new MyClass();
            instance = new WeakReference<MyClass>(classInstance);
    
            //This is now a strong reference and can't be GC'ed between the previous line and this one.
            return classInstance;
        }
    }