Double Checked Locking in Singleton

56,403

Solution 1

No, since you are obtaining lock on the SearchBox.class, only one thread will enter the synchronized block at a time. So the first thread enters then finds searchBox is null and creates it and then leaves the synchronized block, then the second thread enter the block then it finds that the searchBox is not null because the first thread already created it so it will not create a new instance of searchBox.

The double checked pattern is used to avoid obtaining the lock every time the code is executed. If the call are not happening together then the first condition will fail and the code execution will not execute the locking thus saving resources.

Solution 2

Let's look at this code:

1 if (searchBox == null) {
2     synchronized (SearchBox.class) {
3     if (searchBox == null) {
4         searchBox = new SearchBox();
5     }
6 }

Let's try to reason about this. Let's say we have two threads A and B and let's assume that at least one of them reaches line 3 and observes searchBox == null is true. Two threads can not both be at line 3 at the same time because of the synchronized block. This is the key to understanding why double-checked locking works. So, it must the case that either A or B made it through synchronized first. Without loss of generality, say that that thread is A. Then, upon seeing searchBox == null is true, it will enter the body of the statement, and set searchBox to a new instance of SearchBox. It will then eventually exit the synchronized block. Now it will be B's turn to enter: remember, B was blocked waiting for A to exit. Now when it enters the block, it will observe searchBox. But A will have left just having set searchBox to a non-null value. Done.

By the way, in Java, the best way to implement a singleton is to use a single-element enum type. From Effective Java:

While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

Solution 3

This double check lock is only necessary if you are worried about many threads calling the singleton simultaneously, or the cost of obtaining a lock in general.

Its purpose is to prevent unnecessary synchronization, thereby keeping your code fast in a multi-threaded environment.

Check out this link for more information.

If you are running in Java 1.5 or greater, and you use the volatile keyword in your double-check locked mechanism, it will work fine. As you are using the volatile keyword, your example is not broken according to the same link above.

Solution 4

if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. If an instance was already created, don't do anything - avoid locking threads
  2. The first thread that has acquired the lock checks and sees that there is no such object and creates it. It releases the lock and the second one can do the same - it has to check if the object exists because the first one may have created it.

So basically the outer if is used to prevent redundant locks - it lets all thread know that there is already an object and they don't need to lock/do anything. And the inner if is used to let a concurrent thread know whether another has already created the object or not.

Share:
56,403
hqt
Author by

hqt

Those who don't speak math are doomed to speak nonsense. My brief profile: https://github.com/hqt/profile

Updated on July 05, 2022

Comments

  • hqt
    hqt almost 2 years

    here is my custom class for singleton pattern. in this code, I use double-checked locking as below. As I read many posts on some source, they say that double check is useful because it prevents two concurrent threads run at same times make two different objects.

    public class DoubleCheckLocking {
    
        public static class SearchBox {
            private static volatile SearchBox searchBox;
    
            // private constructor
            private SearchBox() {}
    
            // static method to get instance
            public static SearchBox getInstance() {
                if (searchBox == null) { // first time lock
                    synchronized (SearchBox.class) {
                        if (searchBox == null) {  // second time lock
                            searchBox = new SearchBox();
                        }
                    }
                }
                return searchBox;
            }
    }
    

    I still don't understand above code so much. What is the problem, if two threads together run same line of code when instance is null ?

    if (searchBox == null) {
                    synchronized (SearchBox.class) {
                        if (searchBox == null) {
                            searchBox = new SearchBox();
                        }
                    }
                }
    

    When that appear. both two threads will see object is null. then both synchronize. and then, they check again, and still see it null. and create two different objects. OOOPS.

    Please explain for me. What have I understand wrong ?

    Thanks :)

  • William Morrison
    William Morrison almost 11 years
    I don't know much about this, but apparently this type of double checked lock is broken. It doesn't work how you'd expect according to this
  • Arun P Johny
    Arun P Johny almost 11 years
    @WilliamMorrison thanks... haven't seen that before... going through it now
  • jtahlborn
    jtahlborn almost 11 years
    actually, this example is not broken. in the jdk 1.5+ memory model, making the reference volatile (as in the OP's code) "fixes" the double checked locking pattern.
  • William Morrison
    William Morrison almost 11 years
    Ah, @jtahlborn so to be perfectly clear, if the variable being checked is volatile and we're running in 1.5 or greater, this type of double locked pattern is not broken? I am personally curious about this. Again, I know very little about this "problem."
  • jtahlborn
    jtahlborn almost 11 years
    actually, the OP's version does work (as per the article you linked).
  • jtahlborn
    jtahlborn almost 11 years
    @WilliamMorrison - the link in your own answer shows this very example.
  • William Morrison
    William Morrison almost 11 years
    Yeah I just noticed that haha. Hadn't gotten to that point in the article yet. Just did.
  • deeKay
    deeKay about 10 years
    It won't work. Say thread C goes to line 1, while thread A was executing line 4, thread C might see a partially constructed object. As per JMM, only if two threads are locking on the same object they are guaranteed to see same version. The sync to main memory might happen before exit from synchronised block.
  • nikhilvora
    nikhilvora almost 8 years
    What would be the disadvantage if method is declared synchronized with just one null check?
  • Qin Dong Liang
    Qin Dong Liang almost 6 years
    hi @ArunPJohny According to happens-before definition, after one thread releases the lock, another thread acquires the lock, then the latter can see the previous change. If so, I don't think the volatile keyword is needed . Can you explain it meaning of existing in more detail
  • Qin Dong Liang
    Qin Dong Liang almost 6 years
    @jason According to happens-before definition, after one thread releases the lock, another thread acquires the lock, then the latter can see the previous change. If so, I don't think the volatile keyword is needed . Can you explain it meaning of existing in more detail ?
  • Sushant
    Sushant almost 5 years
    So why don't we use "synchronized" keyword on outer if only ? This will also stop multiple threads accessing synchronized block at a time. PLease clarify
  • vijaya kumar
    vijaya kumar almost 2 years
    @nikhilvora if synchronization is at method level lock all other static methods in the class will also be blocked. its a performance hit.