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.
Might be worth adding that double checked locking on the JVM has been known to have some very subtle issues:
ReplyDeletehttp://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Will, that's old news. At your link please look up the section 'Under the new Java Memory Model'.
ReplyDeleteIn 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.
AFAIU, weakCompareAndSet could be used there to be fast.
ReplyDeleteweakCompareAndSet 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*.
Thanks Adriano, I was not aware of this. I'll update the article.
ReplyDeleteI 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