Thursday, June 16, 2011

Lock-less singleton pattern

Although the singleton pattern is now know as an anti-pattern, I think it still is a valid choice when you only need one instance in a particular context. Anyway, in Java there are several ways to implement a singleton.

For example this one uses a synchronized block:

private Singleton singleton = null; protected Singleton createSingleton() { synchronized (this) { // locking on 'this' for simplicity if (singleton == null) { singleton = new Singleton(); } return singleton; } }

To prevent locking all the time you can use the double-check idiom on a modern JVM. But I just thought about another implementation that switches locking for preventing code re-ordering, making it a lock-less implementation:

private AtomicReference singletonRef = new AtomicReference(null); protected Singleton createSingleton() { Singleton singleton = singletonRef.get(); if (singleton == null) { singleton = new Singleton(); if (!singletonRef.weakCompareAndSet(null, singleton)) { singleton = singletonRef.get(); } } return singleton; }

There is only one catch to this implementation: although createSingleton() will always return the same instance, it must be allowed to call the constructor of Singleton twice without side-effects.

Update 2011-06-18: changed compareAndSet to weakCompareAndSet as suggested by Adriano.

5 comments:

  1. Might be worth adding that double checked locking on the JVM has been known to have some very subtle issues:

    http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

    ReplyDelete
  2. Will, that's old news. At your link please look up the section 'Under the new Java Memory Model'.

    In fact my implementation doesn't differ that much from using a volatile. The only difference is that the locking (the synchronized block) is replaced by a compare and swap instruction.

    ReplyDelete
  3. AFAIU, weakCompareAndSet could be used there to be fast.

    weakCompareAndSet atomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables *other than the target of the weakCompareAndSet*.

    ReplyDelete
  4. Thanks Adriano, I was not aware of this. I'll update the article.

    ReplyDelete
  5. I think it is also possible to use a AtomicReferenceFieldUpdater and a volatile field. I am not sure if this is faster than the AtomicReference.get().

    ReplyDelete