[英]Impact of multiple threads using same object in a singleton class
我正在設計一個可以支持不同數據源的模塊。 我的模塊將用戶的公司ID作為輸入,我必須根據公司ID調用相應的類。 我正在嘗試合並一些好的設計並盡可能避免條件語句。
我有一個使用此方法的FetchDataSource單例類。
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()
}
“Communicator”是一個接口,通信器映射將返回適當的實例。 這是同一單例類中的populateCommunicatorMap()方法。
private static void populateCommunicatorMap() {
communicatorMap = new HashMap<String, Communicator>();
communicatorMap.put("AD", new ADCommunicator());
communicatorMap.put("DB2", new DB2Communicator());
communicatorMap.put("MYSQL", new MYSQLCommunicator());
}
ADCommunicator,DB2Communicator和MYSQLCommunicator將實現Communicator接口。
代碼似乎適用於我的測試草案。 我唯一關心的是HashMap將為同一類型的所有通信請求返回相同的對象。 如果我想避免條件語句,我似乎無法避免在hashmap中使用相同的實例。 否則,而不是hashmap,我可以像這樣進行調用。
Communicator comm;
if (type = "AD") comm = new ADCommunicator();
if (type = "DB2") comm = new DB2Communicator();
if (type = "MYSQL") comm = new MYSQLCommunicator();
我通過使用hashmap返回基於類型的實例來避免這種情況。 但是后來我無法避免單例問題,即我得到相同的實例。
在多線程環境中,一次需要支持數十萬個通信請求,考慮到我需要在每個Communicator類中同步大量代碼,這可能是個問題。 有沒有辦法可以避免同步並使其線程安全而不影響性能?
我似乎無法避免在hashmap中使用相同的實例
您可以使用開關而不是一堆ifs。
將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可以直接切換字符串。
// type is-a String
switch(type) {
case "AD": return new ADCommunicator();
case "DB2": return new DB2Communicator();
case "MYSQL": return new MYSQLCommunicator();
default: return null;
}
切換枚舉將與地圖一樣快,如果不是更快。 打開字符串將與Map一樣快。
或者有一張工廠地圖:
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();
或者使用反射API來創建實例,但是那樣使用條件可能會更好。
// 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;
}
PS這聽起來像是過早的優化。 我懷疑確定使用哪個Communicator會成為系統中的瓶頸。
如果所有的通信器都可以使用空參數列表構造函數構造,那么您可以將通信器的類型(類)存儲在映射中而不是實例中。 然后,您可以從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);
}
}
結果:
communicator2 communicates
comm2: pack.Communicator2@6bc7c054
communicator2 communicates
anotherComm2: pack.Communicator2@232204a1
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
剩下的問題是Communicator的實現。 所有這些都假設它也是線程安全的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.