Evgeny Mamaev
Evgeny Mamaev

Reputation: 1357

Double-checked Locking (DCL) and how to fix it

I was reading an article about Double-checked Locking. The problem discussed is as follows:

public class MyFactory {
    private static MyFactory instance;

    public static synchronized MyFactory getInstance() {
        if (instance == null)
            instance = new MyFactory();
        return instance;
    }

    private MyFactory() {}
}

One of the suggested ways how to avoid synchronization on every getInstance() call is to use the class loader:

public class MyFactory {
    private static final MyFactory instance;

    static {
        try {
            instance = new MyFactory();
        } catch (IOException e) {
            throw new RuntimeException("Darn, an error's occurred!", e);
        }
    }

    public static MyFactory getInstance() {
        return instance;
    }

    private MyFactory() throws IOException {
        // read configuration files...
    }
}

Author says: "... the JVM ensures that this static initialisation code is called exactly once, when the class is first referred to and loaded." Which I totally agree. But I don't understand the follows: "Using the class loader is generally my preferred way of dealing with lazy static initialisation."

Is the second code snippet a lazy static initialisation?

Upvotes: 1

Views: 788

Answers (3)

xiaofeng.li
xiaofeng.li

Reputation: 8587

Not exactly. The initialization happens when MyFactory class was loaded, not when getInstance() was called. If you want to initialize the instance when the method was called, you can use a holder class.

public class MyFactory {
    private static final class Holder {
        private static final MyFactory instance = new MyFactory();
    }

    public static MyFactory getInstance() {
        return Holder.instance;
    }
}

Upvotes: 1

Shyam Baitmangalkar
Shyam Baitmangalkar

Reputation: 1073

Playing with Double-checked locking is like playing with fire. There are many ways one can get it wrong.

The purpose of Double-checked locking idiom is to avoid unnecessary synchronization. Thus your first code snippet doesn't fit into the category.

Coming to your second code snippet, if the singleton you are creating is static, then there is a compact solution in which you can define the singleton as a static field in a separate class and not in the same class.

class MyFactorySingleton {
  static MyFactory singleton = new MyFactory();
} 

This semantics of Java guarantees that the field will not be initialized until the field is referenced, and that any thread which accesses the field will see all of the writes resulting from initializing that field.

But from JDK5 and later, Java has started supporting volatile semantics which says that

the value of a variable which is declared volatile will never be cached thread-locally. All reads and writes will go straight to "main memory". Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.

So, a some what fool proof design for a Double-checked looking should look like this:

class MyFactory {
  private volatile MyFactory instance = null;

  public MyFactory getInstance() {
     if(instance == null) {
        synchronized(MyFactory.class) {
           if(instance == null) {
              instance = new MyFactory();
           }
        }
     }
     return instance;
  }
}

Prof. Joshua Bloch and his co-authors explains how Double-checked locking can go wrong in this article. It is worth reading this.

Upvotes: 1

dSH
dSH

Reputation: 143

It is not lazy. This is lazy (using class loader):

public class Singleton {

    public static class SingletonHolder {
        public static final Singleton HOLDER_INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.HOLDER_INSTANCE;
    }
}

Upvotes: 2

Related Questions