Friday, April 10, 2009

Java Singletons

This post doesn't contain any original research, instead, it describes a problem which I encountered. I believe many programmers have dealt with this issue as well, however, the common solution is wrong.

Singleton is a pretty basic design pattern. Apparently, implementing it is not so basic. The trivial implementation in java is rather straight forward:

public class Singleton {
//DO NOT USE - not thread safe
private static Singleton m_instance = null;
//Making the constructor private enforces the singleton property.
private Singleton() {
//Do something
}
public static Singleton getInstance() {
if(null == m_instance) {
m_instance = new Singleton();
}
return m_instance;
}
}
This is of course not thread safe, as two threads can see null value and then two instances will be created. Again, there is a trivial solution, just make the call to getInstance synchronized:


public class Singleton {
//Works, but not very efficient
private static Singleton m_instance = null;
//Making the constructor private enforces the singleton property.
private Singleton() {
//Do something
}
public static synchronized Singleton getInstance() {
if(null == m_instance) {
m_instance = new Singleton();
}
return m_instance;
}
}

Well, this will work. However, the lock will be acquired whenever getInstance is called, which is bad for performance. This problem is enhanced if many threads are accessing the singleton object, so this is still not good enough. Luckily, there is a famous technique to solve this issue, called the "double checked lock". This technique puts the synchronization itself after checking for null, and then checks again for null in the synchronized block itself, so the lock is not acquired most of the time.


public class Singleton {
//DO NOT USE - Does not always work!
private static Singleton m_instance = null;
//Making the constructor private enforces the singleton property.
private Singleton() {
//Do something
}
public static Singleton getInstance() {
if(null == m_instance) {
createInstance();
}
return m_instance;
}
public static synchronized void createInstance() {
//Double check - in case two threads are calling this function.
if(null == m_instance) {
m_instance = new Singleton();
}
}
}

Until recently, I wrongly believed that this is a good technique. However, it does not work! Sometimes, a thread can see a non null value, but the object itself is not yet constructed. This is when Google comes in handy, and apparently the issue is well explained here. To sum it up, until JDK 5 there wasn't really a good solution for this issue in java (It could be solved in languages that gave the programmer the ability to explicitly create memory barriers, and a solution for Java that is based on using ThreadLocal is also suggested there). From JDK 5, there is a simple solution, which is cited from Jeremy Manson's blog. It could simply be solved by setting the instance's variable to be volatile, which ensures that it will remain null, until after the object is constructed. So this is the final solution, which works from JDK 5 and up:


public class Singleton {
//Works from JDK 5 and up
private static volatile Singleton m_instance = null;
//Making the constructor private enforces the singleton property.
private Singleton() {
//Do something
}
public static Singleton getInstance() {
if(null == m_instance) {
createInstance();
}
return m_instance;
}
public static synchronized void createInstance() {
//Double check - in case two threads are calling this function.
if(null == m_instance) {
m_instance = new Singleton();
}
}
}

Until next time...

No comments:

Post a Comment