您的方法不是线程安全的:
private static Map<Class<MyClass<?>>, MyClass<?>> s_instances =
new HashMap<Class<MyClass<?>>, MyClass<?>>();
public static MyClass<?> blah(Class<MyClass<?>> clz)
throws InstantiationException, IllegalAccessException {
if (s_instances.get(clz) != null)
return s_instances.get(clz);
// here1
MyClass<?> instance = clz.newInstance();
s_instances.put(clz, instance);
// here2
return instance;
}
一旦一个线程越过标记的行//here1
,第二个线程可能会在第一个线程到达标记的行之前进入该方法//here2
,因此创建第二个相同类型的“单例”并覆盖映射中的第一个。
快速修复将是在地图上同步:
public static MyClass<?> blah(Class<MyClass<?>> clz)
throws InstantiationException, IllegalAccessException {
synchronized(s_instances){
if (s_instances.get(clz) != null)
return s_instances.get(clz);
// here1
MyClass<?> instance = clz.newInstance();
s_instances.put(clz, instance);
// here2
return instance;
}
}
但是,这意味着许多线程将不得不等待很多时间,最终可能会杀死您的应用程序。可能你应该做的是一个两步的解决方案:
public static MyClass<?> blah(Class<MyClass<?>> clz)
throws InstantiationException, IllegalAccessException {
Object candidate = s_instances.get(clz);
if(clz.isInstance(candidate)){ // implicit null check
return clz.cast(candidate);
}
synchronized(s_instances){
Object candidate = s_instances.get(clz);
if(clz.isInstance(candidate)){ // gotta check a second time in a
return clz.cast(candidate); // synchronized context
}
MyClass<?> instance = clz.newInstance();
s_instances.put(clz, instance);
return instance;
}
}
此外,HashMap 不适合并发访问,因此您应该将其包装在Collections.synchronizedMap()
:
private static Map<Class<MyClass<?>>, MyClass<?>> s_instances =
Collections.synchronizedMap(new HashMap<Class<MyClass<?>>, MyClass<?>>());
或者用 aConcurrentHashMap
代替。