簡體   English   中英

單個類中使用相同對象的多個線程的影響

[英]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。

切換枚舉(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)

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM