Andrey
Andrey

Reputation: 3765

Java, lazily initialized field without synchronization

Sometimes when I need lazily initialized field, I use following design pattern.

class DictionaryHolder {
  private volatile Dictionary dict; // some heavy object

  public Dictionary getDictionary() {
    Dictionary d = this.dict;
    if (d == null) {
      d = loadDictionary(); // costy operation
      this.dict = d;
    }
    return d;
  }
}

It looks like Double Checking idion, but not exactly. There is no synchronization and it is possible for loadDictionary method to be called several times.

I use this pattern when the concurrency is pretty low. Also I bear in mind following assumptions when using this pattern:

My questions:

  1. Is this pattern correct? In other words, is it possible for getDictionary() to return invalid data?
  2. Is it possible to make dict field non-volatile for more efficiency?
  3. Is there any better solution?

Upvotes: 6

Views: 1231

Answers (7)

Andrew White
Andrew White

Reputation: 53496

Just a quick stab at this but what about...

class DictionaryHolder {
  private volatile Dictionary dict; // some heavy object

  public Dictionary getDictionary() {
    Dictionary d = this.dict;
    if (d == null) {
      synchronized (this) {
        d = this.dict;
        if (d == null) { // gated test for null
          this.dict = d = loadDictionary(); // costy operation
        }
    }
    return d;
  }
}

Upvotes: 2

irreputable
irreputable

Reputation: 45443

Your code is correct. To avoid loading more than once, synchronized{} would be nice.

You can remove volatile, if Dictionary is immutable.

private Dictionary dict; // not volatile; assume Dictionary immutable

public Dictionary getDict()
    if(dict==null)
        dict = load()
    return dict;

If we add double checked locking, it's perfect

public Dictionary getDict()
    if(dict==null)
        synchronized(this)
             if(dict==null)
                  dict = load()
    return dict;

Double checked locking works great for immutable objects, without need of volatile.


Unfortunately the above 2 getDict() methods aren't theoretically bullet proof. The weak java memory model will allow some spooky actions - in theory. To be 100% correct by the book, we must add a local variable, which clutters our code:

public Dictionary getDict()
    Dictionary local = dict;
    if(local==null)
        synchronized(this)
             local = dict;
             if(local==null)
                  local = dict = load()
    return local;

Upvotes: 3

Dead Programmer
Dead Programmer

Reputation: 12575

Initialize-on-demand holder class idiom

This method relies on the JVM only intializing the class members upon first reference to the class. In this case, we have a inner class that is only referenced within the getDictionary() method. This means DictionaryHolder will get initialized on the first call to getDictionary().

public class DictionaryHolder {

  private DictionaryHolder ()
  {
  }

  public static Dictionary getDictionary() 
  {
     return DictionaryLazyHolder.instance;
  }

  private static class DictionaryLazyHolder
  {
    static final DictionaryHolder instance = new DictionaryHolder();
  }
}

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533530

The simplest solution is to rely on the fact that a class is not loaded until it is needed. i.e. it is lazy loaded anyway. This way you can avoid having to do those checks yourself.

public enum Dictionary {
 INSTANCE;

  private Dictionary() {
     // load dictionary
  }
}

There shouldn't be a need to make it any more complex, certainly you won't make it more efficient.

EDIT: If Dictionary need to extend List or Map you can do.

public enum Dictionary implements List<String> { }

OR a better approach is to use a field.

public enum Dictionary {
    INSTANCE;
    public final List<String> list = new ArrayList<String>();
}

OR use a static initialization block

public class Dictionary extends ArrayList<String> {
    public static final Dictionary INSTANCE = new Dictionary();
    private Dictionary() { }
}

Upvotes: 3

Sanjay T. Sharma
Sanjay T. Sharma

Reputation: 23208

I personally feel that the Initialization on demand holder idiom is a good fit for this case. From the wiki:

public class Something {

        private Something() {}

        private static class LazyHolder {
                private static final Something INSTANCE = new Something();
        }

        public static final Something getInstance() {
                return LazyHolder.INSTANCE;
        }
}

Though this might look like a pattern intended purely for singleton control, you can do many more cool things with it. For e.g. the holder class can invoke a method which in turn populates some kind of data.

Also, it seems that in your case if multiple threads queue on the loadDictionary call (which is synchronized), you might end up loading the same thing multiple times.

Upvotes: 3

axtavt
axtavt

Reputation: 242696

1.Is this pattern correct? In other words, is it possible for getDictionary() to return invalid data?

Yes if it's okay that loadDictionary() can be called by several threads simultaneously and thus different calls to getDictionary() can return different objects. Otherwise you need a solution with syncronization.

2.Is it possible to make dict field non-volatile for more efficiency?

No, it can cause memory visibility problems.

3.Is there any better solution?

As long as you want a solution without syncronization (either explicit or implicit) - no (as far as I understand). Otherwise, there are a lot of idioms such as using enum or inner holder class (but they use implicit synchronization).

Upvotes: 2

P&#233;ter T&#246;r&#246;k
P&#233;ter T&#246;r&#246;k

Reputation: 116266

Is it possible to make dict field non-volatile for more efficiency?

No. That would hurt visibility, i.e. when one thread initializes dict, other threads may not see the updated reference in time (or at all). This in turn would results in multiple heavy initializations, thus lots of useless work , not to mention returning references to multiple distinct objects.

Anyway, when dealing with concurrency, micro-optimizations for efficiency would be my last thought.

Upvotes: 1

Related Questions