我有一个以下工厂,将从多个线程调用。我想知道它是否是线程安全的。
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}}的调用将是线程安全的。
如果它不是线程安全的,那么如何以最有效的方式使其线程安全?
答案 0 :(得分:2)
简短回答:您应该将holder
声明为final
。然后,共享HashaMap
的所谓发布是安全的。在发布之后,所有线程都可以看到它,并且由于地图的内容不会改变,因此它应该表现得像一个不可变的映射,因此是线程安全的。使地图不可修改的另一个建议也很好。
一些善意的咆哮:尽管您似乎担心工厂的线程安全,但还有一些其他非线程相关问题需要解决(首先)。
无需以这种复杂的方式使用Holder图案。例如,内部类只需要保存您立即构建的实例需要什么?
将holder
重命名为更有意义的内容,例如typeToProvidersMap
。是的,它更冗长,但使代码(更多)可读。
这是Client
的工厂还是Provider
的工厂?如果是后者,请适当地重命名该类。此外,将createNewType()
重命名为createNewProvider()
,因为创建新的提供商就是该方法的目的。
即使这是示例代码,它仍然可以是示范性的。
通过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上(这可能不是最佳解决方案)。