From J2EE Bloger http://j2eeblogger.blogspot.com/2007/10/singleton-vs-multiton-synchronization.html
1. Classic Java singleton synchronization problem
public class Singleton {
private static Singleton instance; /** Prevents instantiating by other classes. */
private Singleton(){} public static synchronized Singleton getInstance() {
if (instance == null){
instance = new Singleton();
} return instance;
}
}
The synchronized keyword on the getInstance() method is the evil. In a multi-thread environment, every thread will be blocked by each other when trying to grab the singleton instance. This is the classic java singleton synchronization problem.
2. The traditional solution using a static variable.
public class Singleton {
/** Prevents instantiating by other classes. */
private Singleton() {} private final static Singleton instance = new Singleton(); public static Singleton getInstance() {
return instance;
}
}
The singleton instance is initialized during the class loading time. No synchronization is necessary on the getInstance level.
3. The classic synchronized multiton.
public class Multiton {
private static final HashMap<Object, Multiton> instances = new HashMap<Object, Multiton>(); private Multiton(){} public static Multiton getInstance(Object key){
synchronized (instances) {
// Our "per key" singleton
Multiton instance;
if ((instance = instances.get(key)) == null) {
// Lazily create instance and add it to the map
instance = new Multiton();
instances.put(key, instance);
} return instance;
}
}
}
The Multiton pattern begins with the concept of the Singleton pattern and expands it into an object pool of keys to objects. Instead of having a single instance per runtime, the Multiton pattern ensures a single instance per key. It simplifies retrieval of shared objects in an application.
public class Multiton {
private static final HashMap<Object, Multiton> instances = new HashMap<Object, Multiton>(); public static Multiton getInstance(Object key) {
// Our "per key" singleton
Multiton instance; if ((instance = instances.get(key)) == null) {
synchronized(instances) {
if ((instance = instances.get(key)) == null) {
instance = new Multiton();
instances.put(key, instance);
}
}
} return instance;
}
}
Yes the double-checked locking does not work for Singleton (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html), but it does work for our Multiton pattern. Reason? We have our intermediate HashMap object sits in between. Actually we can use the HashMap to solve the Singleton double-checked locking problem as well, if we don't already have the more elegant solution using a static variable.
5. An alternative solution: Using the Java 5 ConcurrentMap
public class Multiton {
private static final ConcurrentMap<Object, Multiton> instances = new ConcurrentHashMap<Object, Multiton>(); public static Multiton getInstance(Object key) {
// Our "per key" singleton
if (instances.get(key) == null) {
// Lazily create instance and try to add it to the map
Multiton instance = new Multiton();
instances.putIfAbsent(key, instance);
} return instances.get(key);
}
}
The Java 5 ConcurrentMap.putIfAbsent(), being an atomic operation, returns the previous value associated with the key instead of the new suggested value. This ensures the uniqueness of the Multiton instance inside the ConcurrentMap.