Java 单例类中多个线程使用同一对象的影响

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

我正在设计一个可以支持不同数据源的模块。 我的模块获取用户的公司id作为输入,我必须根据公司id调用相应的类。 我试图结合一些好的设计,尽可能避免使用条件语句

我有一个使用此方法的FetchDataSource singleton类

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