Java 单例类中多个线程使用同一对象的影响
我正在设计一个可以支持不同数据源的模块。 我的模块获取用户的公司id作为输入,我必须根据公司id调用相应的类。 我试图结合一些好的设计,尽可能避免使用条件语句 我有一个使用此方法的FetchDataSource singleton类Java 单例类中多个线程使用同一对象的影响,java,multithreading,design-patterns,singleton,Java,Multithreading,Design Patterns,Singleton,我正在设计一个可以支持不同数据源的模块。 我的模块获取用户的公司id作为输入,我必须根据公司id调用相应的类。 我试图结合一些好的设计,尽可能避免使用条件语句 我有一个使用此方法的FetchDataSource singleton类 public class FetchDataSourceSingleton { private static Map<String, Communicator> communicatorMap; public static Commun
public class FetchDataSourceSingleton {
private static Map<String, Communicator> communicatorMap;
public static Communicator getCommunicatorInstance(String dataSourceType) {
if (communicatorMap == null || communicatorMap.isEmpty())
populateCommunicatorMap();
if (communicatorMap.containsKey(dataSourceType))
return communicatorMap.get(dataSourceType);
return null;
}
.... other methods including populateCommunicatorMap()
}
通过使用hashmap返回基于类型的实例,我避免了这种情况。
但是我无法避免我得到相同实例的单例问题
在一个一次需要支持数十万个通信请求的多线程环境中,考虑到我需要同步每个通信器类中的大量代码,这可能是一个问题。
有没有一种方法可以避免同步并使其线程安全而不影响性能?Assylias使用静态初始值设定项是正确的。它在类加载时运行,这保证在类发生任何其他事情之前加载映射 你没有显示地图的声明;我假设它是静态的
private final static Map<String, Communicator> communicatorMap;
static {
communicatorMap = new HashMap<>();
communicatorMap.put("AD", new ADCommunicator());
communicatorMap.put("DB2", new DB2Communicator());
communicatorMap.put("MYSQL", new MYSQLCommunicator());
}; // populated on class-load. Eliminates race from lazy init
私有最终静态地图通信协议;
静止的{
communicatorMap=newhashmap();
communicatorMap.put(“AD”,新的ADCommunicator());
communicatorMap.put(“DB2”,新的DB2Communicator());
communicatorMap.put(“MYSQL”,新的MYSQLCommunicator());
}; // 在类加载时填充。从懒惰初始化中消除竞争
剩下的问题是通信器的实现。所有这些都假设它也是线程安全的
我似乎无法避免hashmap中有相同的实例
您可以使用一个开关,而不是一堆ifs
切换枚举(Java 5)
将type
更改为Java 5+中的枚举,然后可以打开它。一般来说,为了类型安全,我建议使用枚举
// type is-a enum Communicator.TYPE
switch(type) {
case AD: return new ADCommunicator();
case DB2: return new DB2Communicator();
case MYSQL: return new MYSQLCommunicator();
default: return null;
}
切换字符串(Java 8)
Java8可以直接切换字符串
// type is-a String
switch(type) {
case "AD": return new ADCommunicator();
case "DB2": return new DB2Communicator();
case "MYSQL": return new MYSQLCommunicator();
default: return null;
}
切换枚举将与映射一样快,如果不是更快的话。打开字符串的速度将与地图一样快
工厂地图(工厂中的工厂)
或者有一张工厂地图:
private final static Map<String, Factory<? extends Communicator>> map;
static {
map.put("AD", ADCommunicatorFactory.getInstance());
//...
map.put(null, NullFactory<Communicator>.getInstance());
} // populated on class-load. Eliminates race from lazy init
// on get
return map.get(type).make();
private final static Map如果所有通信器都可以用空参数列表构造函数构造,那么可以在映射中存储通信器的类型(类),而不是实例。然后,您可以从communicatorMap中查找类型(java.lang.Class),并使用java.lang.Class.newInstance()实例化一个新实例
例如:
public interface Communicator {
void communicate();
}
public class Communicator1 implements Communicator {
public void communicate() {
System.out.println("communicator1 communicates");
}
}
public class Communicator2 implements Communicator {
public void communicate() {
System.out.println("communicator2 communicates");
}
}
public class CommuniicatorTest {
public static void main(String[] args) throws Exception {
Map<String, Class<? extends Communicator>> communicators = new HashMap<String, Class<? extends Communicator>>();
communicators.put("Comm1", Communicator1.class);
communicators.put("Comm2", Communicator2.class);
Communicator comm2 = communicators.get("Comm2").newInstance();
comm2.communicate();
System.out.println("comm2: " + comm2);
Communicator anotherComm2 = communicators.get("Comm2").newInstance();
anotherComm2.communicate();
System.out.println("anotherComm2: " + anotherComm2);
}
}
您还可以有一个工厂工厂,其中地图中的值是实际通信器
实例的工厂。它将被委托给CommunicatorFactory
s来选择是返回单例Communicator
,还是按需返回新实例,或者介于两者之间。与其懒洋洋地填充hashmap,只需在静态块中一次性填充它(并确保初始化后永远不会访问它)。那么,只要XxxCommunicator类是线程安全的。Side注释,if(communicatorMap.containsKey(dataSourceType))返回communicatorMap.get(dataSourceType);返回null
与返回communicatorMap.get(dataSourceType)完全相同
>“我唯一关心的是HashMap会将所有通信请求的同一对象返回到同一类型。”我很困惑,您是否想要源代码的单个实例?@dozer否我指的是静态块:静态{/*初始化映射*/}
。不是静态方法,谢谢。最后一点看起来很有趣,但我得到了这个错误。类型映射中的方法put(String,Class)不适用于每个put调用的参数(String,Class)。我做错了什么?没关系-我应该用Map@Dozer固定的。对不起,我没有你的类来编译。太棒了。我试试这个。谢谢
private final static Map<String, Factory<? extends Communicator>> map;
static {
map.put("AD", ADCommunicatorFactory.getInstance());
//...
map.put(null, NullFactory<Communicator>.getInstance());
} // populated on class-load. Eliminates race from lazy init
// on get
return map.get(type).make();
// on init
Map<String, Class<? extends Communicator>> map = new HashMap<>();
map.put("AD", ADCommunicator.class);
// on get
try {
return (Communicator) map.get(type).newInstance();
} catch(InstantiationException | IllegalAccessException | NullPointerException e) {
return null;
}
public interface Communicator {
void communicate();
}
public class Communicator1 implements Communicator {
public void communicate() {
System.out.println("communicator1 communicates");
}
}
public class Communicator2 implements Communicator {
public void communicate() {
System.out.println("communicator2 communicates");
}
}
public class CommuniicatorTest {
public static void main(String[] args) throws Exception {
Map<String, Class<? extends Communicator>> communicators = new HashMap<String, Class<? extends Communicator>>();
communicators.put("Comm1", Communicator1.class);
communicators.put("Comm2", Communicator2.class);
Communicator comm2 = communicators.get("Comm2").newInstance();
comm2.communicate();
System.out.println("comm2: " + comm2);
Communicator anotherComm2 = communicators.get("Comm2").newInstance();
anotherComm2.communicate();
System.out.println("anotherComm2: " + anotherComm2);
}
}
communicator2 communicates
comm2: pack.Communicator2@6bc7c054
communicator2 communicates
anotherComm2: pack.Communicator2@232204a1