Example 6. Example 5's output
Buildfile: build.xml
init:
[echo] Build 20030414 (14-04-2003 03:06)
compile:
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-2: created singleton: Singleton@7e5cbd
INFO Thread-1: created singleton: Singleton@704ebb
junit.framework.AssertionFailedError: expected:<true> but was:<false>
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.Assert.failNotEquals(Assert.java:282)
at junit.framework.Assert.assertEquals(Assert.java:64)
at junit.framework.Assert.assertEquals(Assert.java:149)
at junit.framework.Assert.assertEquals(Assert.java:155)
at SingletonTest$SingletonTestRunnable.run(Unknown Source)
at java.lang.Thread.run(Thread.java:554)
[java] .
[java] Time: 0.577
[java] OK (1 test)
Now that we know Example 4's singleton is not thread-safe, let's see how we can fix it.
Synchronization
Making Example 4's singleton class thread-safe is easy—just synchronize the getInstance()
method like this:
public synchronized static Singleton getInstance() {
if(singleton == null) {
simulateRandomActivity();
singleton = new Singleton();
}
logger.info("created singleton: " + singleton);
return singleton;
}
After we synchronize the getInstance()
method, we can rerun Example 5's test case with the following results:
Buildfile: build.xml
init:
[echo] Build 20030414 (14-04-2003 03:15)
compile:
[javac] Compiling 2 source files
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-1: created singleton: Singleton@ef577d
INFO Thread-2: created singleton: Singleton@ef577d
[java] .
[java] Time: 0.513
[java] OK (1 test)
This time, the test case works and our multithreading worries are over; however, the astute reader may realize that the getInstance()
method only needs to be synchronized the first time it is called. Because synchronization is very expensive performance-wise (synchronized methods can run up to 100 times slower than unsynchronized methods), perhaps we can introduce a performance enhancement that only synchronizes the singleton assignment in getInstance()
.
A performance enhancement
In search of a performance enhancement, you might choose to rewrite the getInstance()
method like this:
public static Singleton getInstance() {
if(singleton == null) {
synchronized(Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
Instead of synchronizing the entire method, the preceding code fragment only synchronizes the critical code. However, the preceding code fragment is not thread-safe. Consider the following scenario: Thread 1 enters the synchronized block, and, before it can assign the singleton
member variable, the thread is preempted. Subsequently, another thread can enter the if
block. The second thread will wait for the first thread to finish, but we will still wind up with two distinct singleton instances. Is there a way to fix this problem? Read on.
Double-checked locking
Double-checked locking is a technique that, at first glance, appears to make lazy instantiation thread-safe. That technique is illustrated in the following code fragment:
public static Singleton getInstance() {
if(singleton == null) {
synchronized(Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
What happens if two threads simultaneously access getInstance()
? Imagine Thread 1 enters the synchronized block and is preempted. Subsequently, a second thread enters the if
block. When Thread 1 exits the synchronized block, Thread 2 makes a second check to see if the singleton
instance is still null
. Since Thread 1 set the singleton
member variable, Thread 2's second check will fail, and a second singleton will not be created. Or so it seems.
Unfortunately, double-checked locking is not guaranteed to work because the compiler is free to assign a value to the singleton
member variable before the singleton's constructor is called. If that happens, Thread 1 can be preempted after the singleton reference has been assigned, but before the singleton is initialized, so Thread 2 can return a reference to an uninitialized singleton instance.
Since double-checked locking is not guaranteed to work, you must synchronize the entire getInstance()
method. However, another alternative is simple, fast, and thread-safe.
An alternative thread-safe singleton implementation
Example 7 lists a simple, fast, and thread-safe singleton implementation:
Example 7. A simple singleton
public class Singleton {
public final static Singleton INSTANCE = new Singleton();
private Singleton() {
// Exists only to defeat instantiation.
}
}
The preceding singleton implementation is thread-safe because static member variables created when declared are guaranteed to be created the first time they are accessed. You get a thread-safe implementation that automatically employs lazy instantiation; here's how you use it:
Singleton singleton = Singleton.INSTANCE;
singleton.dothis();
singleton.dothat();
...
Of course, like nearly everything else, the preceding singleton is a compromise; if you use that implementation, you can't change your mind and allow multiple singleton instances later on. With a more conservative singleton implementation, instances are obtained through a getInstance()
method, and you can change those methods to return a unique instance or one of hundreds. You can't do the same with a public static member variable.
You can safely use Example 7's singleton implementation or Example 1's implementation with a synchronized getInstance()
method. However, we must explore another issue: You must specify the singleton class at compile time, which is not very flexible. A registry of singletons will let us specify singleton classes at runtime.
Use a registry
Use a singleton registry to:
- Specify singleton classes at runtime
- Prevent singleton subclasses from allowing multiple instances
Example 8 lists a singleton class that maintains a registry of singletons, registered by class name:
Example 8. A singleton with a registry
import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
private static HashMap map = new HashMap();
private static Logger logger = Logger.getRootLogger();
protected Singleton() {
// Exists only to thwart instantiation
}
public static synchronized Singleton getInstance(String classname) {
if(classname == null) throw new IllegalArgumentException("Illegal classname");
Singleton singleton = (Singleton)map.get(classname);
if(singleton != null) {
logger.info("got singleton from map: " + singleton);
return singleton;
}
if(classname.equals("SingeltonSubclass_One"))
singleton = new SingletonSubclass_One();
else if(classname.equals("SingeltonSubclass_Two"))
singleton = new SingletonSubclass_Two();
map.put(classname, singleton);
logger.info("created singleton: " + singleton);
return singleton;
}
// Assume functionality follows that's attractive to inherit
}
The preceding base class creates subclass instances and stores them in a map. But that base class is high maintenance because you must update its getInstance()
method for every subclass. Luckily, we can use reflection to skirt that issue.
Use reflection
Example 9 lists a singleton with a registry that uses reflection to instantiate a particular class's objects. With this implementation, as opposed to Example 8, the Singleton.getInstance()
method does not need to update when new subclasses are implemented.
Example 9. Use reflection to instantiate singletons
import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
private static HashMap map = new HashMap();
private static Logger logger = Logger.getRootLogger();
protected Singleton() {
// Exists only to thwart instantiation
}
public static synchronized Singleton getInstance(String classname) {
Singleton singleton = (Singleton)map.get(classname);
if(singleton != null) {
logger.info("got singleton from map: " + singleton);
return singleton;
}
try {
singleton = (Singleton)Class.forName(classname).newInstance();
}
catch(ClassNotFoundException cnf) {
logger.fatal("Couldn't find class " + classname);
}
catch(InstantiationException ie) {
logger.fatal("Couldn't instantiate an object of type " + classname);
}
catch(IllegalAccessException ia) {
logger.fatal("Couldn't access class " + classname);
}
map.put(classname, singleton);
logger.info("created singleton: " + singleton);
return singleton;
}
}
One more thing concerning singleton registries: they should be encapsulated in their own class for maximum reuse.
Example 10. A SingletonRegistry class
import java.util.HashMap;
import org.apache.log4j.Logger;
public class SingletonRegistry {
public static SingletonRegistry REGISTRY = new SingletonRegistry();
private static HashMap map = new HashMap();
private static Logger logger = Logger.getRootLogger();
protected SingletonRegistry() {
// Exists to defeat instantiation
}
public static synchronized Object getInstance(String classname) {
Object singleton = map.get(classname);
if(singleton != null) {
return singleton;
}
try {
singleton = Class.forName(classname).newInstance();
logger.info("created singleton: " + singleton);
}
catch(ClassNotFoundException cnf) {
logger.fatal("Couldn't find class " + classname);
}
catch(InstantiationException ie) {
logger.fatal("Couldn't instantiate an object of type " +
classname);
}
catch(IllegalAccessException ia) {
logger.fatal("Couldn't access class " + classname);
}
map.put(classname, singleton);
return singleton;
}
}
Notice I implemented the SingletonRegistry
class as a singleton. I also generalized the registry so it can store and retrieve any type of object. Example 11 shows a Singleton
class that uses the registry:
Example 11. A Singleton class that uses the registry
import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
protected Singleton() {
// Exists only to thwart instantiation.
}
public static Singleton getInstance() {
return (Singleton)SingletonRegistry.REGISTRY.getInstance(classname);
}
}
The preceding Singleton
class uses the registry's singleton instance to retrieve singleton objects by class name.
Now that we've seen how to implement thread-safe singletons and how to use a registry to specify singleton class names at runtime, let's examine how to deal with classloaders and serialization.
Classloaders
Because multiple classloaders are commonly used in many situations—including servlet containers—you can wind up with multiple singleton instances no matter how carefully you've implemented your singleton classes. If you want to make sure the same classloader loads your singletons, you must specify the classloader yourself; for example:
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
The preceding method tries to associate the classloader with the current thread; if that classloader is null
, the method uses the same classloader that loaded a singleton base class. The preceding method can be used instead of Class.forName()
.
Serialization
If you serialize a singleton and then deserialize it twice, you will have two instances of your singleton, unless you implement the readResolve()
method, like this:
Example 12. A serializable singleton
import org.apache.log4j.Logger;
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
// Exists only to thwart instantiation.
}
private Object readResolve() {
return INSTANCE;
}
}
The previous singleton implementation returns the lone singleton instance from the readResolve()
method; therefore, whenever the Singleton
class is deserialized, it will return the same singleton instance.
Example 13 tests Example 12's singleton: