简体   繁体   中英

What if we use only the outer null check in double check singleton pattern?

Question 1: Why in singleton pattern for multi-threading we need two null checks? What if we use only the outer check?

    if (instance == null) {
        synchronized (ABC.class) {

            // What if we remove this check?
            if (instance == null) {
                instance = new ABC();
            }
    }

Question 2: What's the difference between the following:

1: Using directly the class name inside synchronized()

    public ABC getInstance() {
        if (instance == null) {
            // Difference here
            synchronized (ABC.class) {
                if (instance == null) {
                    instance = new ABC();
                }
            }
         }
         return instance;
    }

2: Using a static final Object inside synchronized()

    private static final Object LOCK = new Object();
    .
    .
    public ABC getInstance() {
        if (instance == null) {

             // Difference here
             synchronized (LOCK) {
                if (instance == null) {
                    instance = new ABC();
                }
             }
         }
         return instance;
    }

3: Using new Object() inside synchronized()

    if (instance == null) {
    // Difference here
         synchronized (new Object()) {
            if (instance == null) {
                instance = new ABC();
            }
        }
     }
  1. Removing the inner nullcheck can result in a race condition. Imagine the following scenario: Two threads try to get an instance of your object at the exact same time, so they both check if instance is equal to null and get a true answer, so both threads will try to create the instance of your object. Because this code is synchronized, one of these threads will enter the synchronized block of code while the other waits for the lock to be released. When the first thread is done instantiating and returning the instance of your object, the lock will be released and the second thread will execute the synchronized block, so it will create a new instance and return it because it is not aware that it was previously created while it was waiting for its turn.
  2. Using the class as the argument in synchronized will generate a static lock. This means that all instances of the class will be sharing the lock.
  3. Using an object as the argument for synchronization is ueful if you want lock your synchronized block with a specific object instead of the class or this. This allows you to have different blocks of code using different locks in the same class. For example:

     Object o1 = new Object(); Object o2 = new Object(); synchronized(o1) { //your synchronized block } synchronized(o2){ //other synchronized block } 

In the previous code sample, block1 and block2 can be executing at the same time by different threads because they use a different lock object. If you were using the same lock for both blocks of code (IE the class), block 1 would be blocked until block2 completes its execution, and the other way around.

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