Tarun Chawla
Tarun Chawla

Reputation: 538

Double checking way of creating singleton issue

How reading (outside critical resource block) and writing (inside critical resource block) does not have atomicity issues.

I have read and discussed with various people but most people don't answer if both operations are atomic and how atomicity is actually achieved for above problem.

class ABC {
    private static volatile ABC abcInstance;
    static ABC getInstance(){
        if(abcInstance == null){
            synchronized(ABC.class){
                if(abcInstance == null){
                    abcInstance = new ABC();
                    return abcInstance;
                }
            }
        }
        return abcInstance;
    }

}

Are if(abcInstance == null) outside synchronisation block and abcInstance = new ABC(); atomic, if not then this way of creating singletons is wrong.

In C++, abcInstance = new ABC(); consists of three instructions broadly speaking:

  1. Create ABC object.
  2. Allocate memory for ABC.
  3. Assign it to abcInstance.

And for optimisations compiler can reorder these three instructions in any way. Suppose it follows 2->3->1 and after instruction 3 interrupt happens and next thread calling getInstance() will read that abcInstance has some value then it will point to something which does not have ABC object.

Please correct me if am wrong for both C++ and Java.

Upvotes: 0

Views: 117

Answers (2)

Stephen C
Stephen C

Reputation: 719336

This answers the Java part of your question only.

Is if(abcInstance == null) and abcInstance = new ABC(); are atomic, if not then this way of creating singleton is wrong.

It is not atomicity that is the (potential) problem. (Reference assignment is atomic from the perspective of both the thread doing the assignment, and the thread reading the assigned variable.)

The problem is when the value written to abcInstance becomes visible to another thread.

  • Prior to Java 5, the memory model did not provide sufficient guarantees about memory visibility for that implementation to work reliably.

  • In the Java 5 (and later) memory model, there is a happens before relation between one thread's write to a volatile variable and another thread's subsequent read of the variable. This means:

    1. The second thread is guaranteed to see the non-null value of abcInstance if the first thread has written it.
    2. The happens before relation also guarantees that the second thread will see the fully initialized state of the ABC instance create by the first thread.
    3. The synchronized block ensures that only one ABC instance may be created at a time.

This is the authoritative article explaining why old double-checked locking implementations were broken:


As Andrew Turner states, there is a simpler, cleaner way to implement singleton classes in Java: use an enum.

Upvotes: 2

Ted Lyngmo
Ted Lyngmo

Reputation: 117658

Here are two typical singleton variants in C++.

First one shared by all threads:

class singleton {
private:
    singleton() {}

public:
    singleton(const singleton&) = delete;

    static singleton& get_instance() {
        static singleton ins;
        return ins;
    }
};

And here's one that that will create one instance per thread that needs it:

class tl_singleton {
private:
    tl_singleton() {}

public:
    tl_singleton(const tl_singleton&) = delete;

    static tl_singleton& get_instance() {
        static thread_local tl_singleton ins;
        return ins;
    }
};

Upvotes: 1

Related Questions