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.

As we noticed from the above scriptlet, the access to the getInstance() method is synchronized to ensure the uniqueness of instance per key. This, again, is a performance bottleneck in multi-thread environment. The traditional 'static' solution for Singleton pattern can't be used here since the keys are passed into the Multiton on-the-fly. We have no way to predict how many keys will be passed into the Multiton during the runtime.
 
 
4. The double-check locing solution
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.

In this solution, before a key object has been associated with a Multiton object, if two or more threads are trying to retrieve the instance with the same key object, it's possible that multiple Multiton instance will be created inside the if() block. However, the ConcurrentMap ensures that a same Multiton instance is  to be returned to the caller. Extra instances are eligible for Garbage Collection as soon as the method returns. You may not want to use this solution if the creation of the Multiton instance is an expensive operation, as in certain situation.
 
 

 

posted @ 2015-07-17 11:26  kid551  阅读(361)  评论(0编辑  收藏  举报