Multiton & Singleton
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.