如何使工厂线程安全?

时间:2016-12-23 23:51:31

标签: java multithreading thread-safety

我有一个以下工厂,将从多个线程调用。我想知道它是否是线程安全的。

public class ClientFactory {
  private Map<TypeName, Provider> holder = new HashMap<>();

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

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

  private ClientFactory() {
    holder.put(TypeName.typeA, new DataProvider());
    holder.put(TypeName.typeB, new ProcessProvider());
  }

  public IProcess createNewType(TypeName typeName) {
    Provider provider = holder.get(typeName);
    if (provider == null) {
      throw new IllegalArgumentException("invalid typeName is passed");
    }
    IProcess process = new ProcessImpl(typeName.name(), provider);
    return process;
  }
}

在我的上面工厂createNewType方法将从多个线程调用。我想让它线程安全。它是线程安全的吗?一旦在HashMap构造函数中填充ClientFactory,我就不会修改它。所以我认为它是线程安全的,因为对get()的{​​{1}}的调用将是线程安全的。

如果它不是线程安全的,那么如何以最有效的方式使其线程安全?

3 个答案:

答案 0 :(得分:2)

简短回答:您应该将holder声明为final。然后,共享HashaMap的所谓发布是安全的。在发布之后,所有线程都可以看到它,并且由于地图的内容不会改变,因此它应该表现得像一个不可变的映射,因此是线程安全的。使地图不可修改的另一个建议也很好。

一些善意的咆哮:尽管您似乎担心工厂的线程安全,但还有一些其他非线程相关问题需要解决(首先)。

  1. 无需以这种复杂的方式使用Holder图案。例如,内部类只需要保存您立即构建的实例需要什么?

  2. holder重命名为更有意义的内容,例如typeToProvidersMap。是的,它更冗长,但使代码(更多)可读。

  3. 这是Client的工厂还是Provider的工厂?如果是后者,请适当地重命名该类。此外,将createNewType()重命名为createNewProvider(),因为创建新的提供商就是该方法的目的。

  4. 即使这是示例代码,它仍然可以是示范性的。

    通过1),我的意思是以开始更好

    public class ClientFactory {
      private final Map<TypeName, Provider> typeToProvider = new HashMap<>();
      // constructing the singleton since the construction needn't be lazy
      private static final ClientFactory INSTANCE = new ClientFactory();
    
      public static ClientFactory getInstance() {
        return INSTANCE;
      }
    // rest is omitted for brevity
    }
    

    当需要延迟初始化并考虑到特定目的时,将使用Holder模式。

答案 1 :(得分:-1)

是的,但是你可以通过声明变量final来改进地图,这样就不能为变量分配其他Map并使用不可修改的地图初始化它:

private final Map<TypeName, Provider> holder;

private ClientFactory() {
    Map<TypeName, Provider> tmp = new HashMap<>();
    holder.put(TypeName.typeA, new DataProvider());
    holder.put(TypeName.typeB, new ProcessProvider());

    this.holder = Collections.unmodifiableMap(tmp);
}

答案 2 :(得分:-1)

如果未修改HashMap,则Factory类是Thread安全的。虽然问题可能在使用地图中的对象时开始(因为它们不是线程安全的,即使Map实现是线程安全的)。

因此,请确保Provider实现是线程安全的,或者将整个Provider实例锁定在access上(这可能不是最佳解决方案)。