简体   繁体   中英

What's wrong with the following getInstance() method

I've been studying for a job interview and I've found this question:

What's wrong with the following Singleton factory method getInstance()?

public class Singleton {

    private static Singleton mySingleton;

    protected Singleton() {}

    public static Singleton getInstance() {
        if (mySingleton == null) {
           synchronized(Singleton.class) {
             if(mySingleton == null) {
                mySingleton = new Singleton();
             }
           }
        }
        return mySingleton
    }   
}

I know the constructor is wrong and should be private. However, they're asking about what's wrong with the getInstance() method, everything looks fine to me, I've seen a lot of examples like this one (in a multithreading environement) what am I missing?

The largest problem with the original code is that it is a classic example of the Double Check anti-pattern. It looks great but is just broken. More on that here: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Note that as of java 1.5 there is a way to finesse it, the synchronized pattern or better static initialization is really the way to go.

-edit (I just noticed this is studying for a job interview. Pulling out "double check" if they show this example would be good. If asked for the details, I would go with complex java memory issues you did not fully understand but accept the fact to avoid double check . Good luck)

This does appear to be thread-safe, however the code is more complex than it needs to be. Plus the use of a lock, comes with a performance cost. It takes time to acquire the lock, and other threads attempting to execute that code are blocked.

These 2 concerns could be addressed by using a static initializer and removing synchronization from the getInstance method entirely. The JVM will ensure that this statement is executed exactly once. The result is significantly simpler code, and slightly more-performant code.

public class Singleton {

    private static Singleton mySingleton = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return mySingleton;
    }   
}

what am I missing?

Possible memory effects described in JLS 17 , so there is no happens-before relationship between write and read to mySingleton. It is counterintuitive, but if you checked that mySingleton is not null, you can see wrong object. Ie mySingleton in this example can be written and read at the same time, it is called data-race. To fix this program you can just add volatile. According specifications it creates happens-before relationship between write and read.

Isn't what you have a bit too redundant? How about this instead?

public static synchronized Singleton getInstance()
    {
        if (mySingleton == null)
            mySingleton = new Singleton();

        return mySingleton;
    }

or...

public static synchronized Singleton getInstance()
    {
        return mySingleton==null?mySingleton=new Singleton():mySingleton;
    }

compliments to Mr. Polywhirl

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM