Best Practice : Thread safe Multiton pattern with lazy initialization

848 Views Asked by At

Idea is to create a factory class that returns different singleton instances based on a 'type'. [Multiton pattern]. Also, the singleton instances should be lazily created.

Is the below code thread safe? Using ConcurrentHashMap makes it simpler but I want to try with HashMap.

public class Multiton {

    private HashMap<String, Interface> interfaceMap;

    private static class Holder {
        public static final Multiton INSTANCE = new Multiton();
    }

    public static Multiton getInstance() {
        return Holder.INSTANCE;
    }

    private Multiton(){
        interfaceMap = new HashMap<String, Interface>();        
    }

    public Interface getInterface(String key){
        Interface value = interfaceMap.get(key);
        if(null == value){
            synchronized(interfaceMap){
                // double check locking
                if(interfaceMap.get(key) == null){
                    switch(key){
                    case TypeA : // initialize Type A interface here and put it to map
                        value = new TypeA();
                        interfaceMap.put(key, value);
                        break;
                    case TypeB : // initialize Type B interface here and put it to map
                        value = new TypeB();
                        interfaceMap.put(key, value);
                        break;
                    }
                }
            }
        }
        return interfaceMap.get(key);
    }
}
1

There are 1 best solutions below

5
On

Double checked locking is (in general) not thread safe. In particular, it is not sufficient to only synchronize when writing to a map, as that does not prevent a reader thread from accessing the state a writer thread is currently modifying.

If you can't use a ConcurrentHashMap (for instance because you must ensure that a single TypeA is created, not just that a single TypeA is used by all clients), the following should work and be reasonably cheap. In particular, it is lock free once the lazy object has been constructed.

abstract class Lazy<T> {
    private volatile T instance;

    abstract T create();

    void get() {
        if (instance == null) {
            synchronized (this) {
                if (instance == null) {
                    instance = create();
                }
            }
        }
        return instance;
    }
}

class Multiton {
    private final Map<String, Lazy<?>> map = new HashMap<>();

    Multiton() {
        map.put("A", new Lazy<A> {
            A create() {
                return new A();
            }
        }
        map.put("B", new Lazy<B> {
            B create() {
                return new B();
            }
        }
        // and so on for the other types
    }
}

For production quality, you might want to consider using lamdba expressions for the factory methods, and type safe keys for the map (such as the class objects of the interface requested).