Search code examples
javaconcurrencyvolatile

why java double check lock singleton must use the volatile keyword?


I have seen some similar questions,but I still have some confusions.
code is here:

private volatile static DoubleCheckSingleton instance;

private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance(){

    if(instance==null){ //first

        synchronized (DoubleCheckSingleton.class){

            if(instance==null){  // second
                instance=new DoubleCheckSingleton();
            }

        }

    }

    return instance;

}

In this question Why is volatile used in double checked locking, it says that without a volatile keyword, a thread may assign the instance variable before the constructor finishes, so another thread may see a half-constructed object, which can cause serious problem.

But I don't understand how volatile can solve the problem. Volatile is used to ensure visibility, so when thread A assign a half-constructed object to instance variable, the other thread can immediately see the change, which makes the situation worse.

How does volatile solve the problem, somebody please explain to me. Thanks!


Solution

  • a thread may assign the instance variable before the constuctor finishes

    That's not actually true. The assignment is right there in the code example:

    instance=new DoubleCheckSingleton()
    

    Obviously, the thread that performs that assignment can not possibly do the assignment before the constructor call has returned.

    The problem is, when two different threads are running on two different processors without any synchronization, they will not necessarily agree upon the order in which assignments happen. So, even though thread A assigned the fields of the new object (inside the new DoubleCheckSingleton() call) before it assigned instance, thread B potentially could see those assignments out-of-order. Thread B could see the assignment to instance before it sees some of the other things that new DobuleCheckSingleton() did.

    Declaring instance to be volatile synchronizes the threads. volatile guarantees that everything thread A did before it assigned a volatile variable will become visible to thread B when thread B fetches the value of the volatile variable.