2014-08-27 70 views
0

想法是创建一个基于'type'返回不同单例实例的工厂类。 [Multiton模式]。另外,单例实例应该被延迟创建。最佳实践:使用延迟初始化的线程安全Multiton模式

下面的代码线程安全吗?使用ConcurrentHashMap使其更简单,但我想尝试使用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); 
    } 
} 
+0

使用Spring已经为你处理这个。 – 2014-08-27 22:07:53

+0

该应用程序是一个纯Java应用程序。没有春天请愿。 – user170008 2014-08-27 22:08:59

+0

从Spring或其他已经处理此问题的框架修改必要的源代码以解决您的问题。而且,Spring是使用纯Java构建的。 – 2014-08-27 22:09:43

回答

0

Double checked locking is (in general) not thread safe。特别是,仅在写入映射时进行同步是不够的,因为这并不妨碍读者线程访问写入器线程当前正在修改的状态。

如果您不能使用ConcurrentHashMap(例如,因为您必须确保创建了一个TypeA,而不仅仅是所有客户端都使用一个TypeA),则以下内容应该可以工作并且价格合理。特别是,一旦构建了懒惰对象,它就是无锁的。

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 
    } 
} 

生产质量,你可能要考虑使用lamdba表达式的工厂方法,以及地图(如要求接口的类的对象)类型安全密钥。

+0

对于Java 1.4及更早版本,这是正确的。由于1.5 DCL正常工作。我不想让你失望,但这个答案是错误的,你应该删除它。 – Kayaman 2014-08-27 22:39:52

+0

我链接的文章明确指出,只有在非常特殊的情况下,双重检查锁定才是正确的。在这种情况下这是不正确的。 – meriton 2014-08-27 22:52:22

+0

你链接的文章是古老的。由于改进了Java 1.5的内存模型,因此该文章已过时。 – Kayaman 2014-08-27 22:57:38