我们正在使用HashMap缓存方法上的注释查找。使用Spring的AnnotationUtils.findAnnotation检索注释。不使用缓存会导致性能大幅下降。
我们的实现看起来像是:
public class SomeService {
// Caches annotations on methods. The value can be null!
private static final Map<Method, MyAnnotation> ANNOTATION_CACHE = new HashMap<Method, MyAnnotation>();
private MyAnnotation findAnnotation(Method m) {
if (ANNOTATION_CACHE.containsKey(m)) {
return ANNOTATION_CACHE.get(m);
}
MyAnnotation a = AnnotationUtils.findAnnotation(m, MyAnnotation.class);
ANNOTATION_CACHE.put(m, a);
return a;
}
public void doSomethingWith(Class<?> clazz) {
for (Method m : clazz.getMethods()) {
MyAnnotation a = findAnnotation(m);
if (a != null) {
// do something with annotation a
}
}
}
}
现在的问题是我是否需要同步对ANNOTATION_CACHE Map的访问。可能发生的最糟糕的事情是并行的两个线程将相同的(m,a)对放入缓存映射中,这不会受到伤害,不是吗?
我的第一个想法是使用ConcurrentHashMap,但它不允许空值(如果方法没有注释=&gt; null,则需要此值)。使用Collections.synchronizedMap()并同步对地图的每次访问也不理想,因为这种doSomethingWith()方法被频繁调用。
在这种情况下,是否真的有必要同步对HashMap的访问?缓存必须在运行时更改,键/值对只插入一次从不删除但会被多次读取。
有什么想法吗?
答案 0 :(得分:3)
如果您有一个单独的阶段,其中地图是专门写入的,然后是另一个阶段,其中地图是专门从中读取的,那么您不需要并发集合。 您还可以通过使用不可变的映射写后阶段将映射保存为未修改映射。
例如,使用Guava的Immutable map:
ImmutableMap.copyOf(map);
如果您预见到对集合的并发读/写/删除访问权限,那么您肯定应该使用ConcurrentHashMap;因为读/写/删除操作不是原子操作,所以最终会得到一些非常奇怪的结果。
我的第一个想法是使用ConcurrentHashMap,但它不允许空值(如果方法没有注释,则需要此值=&gt; null)
然后首先不插入空值;更好的是,从地图中删除现有的密钥。
答案 1 :(得分:0)
现在的问题是我是否需要同步访问权限 ANNOTATION_CACHE地图与否。可能发生的最糟糕的事情是 并行的两个线程将相同的(m,a)对放入缓存映射中, 这不会伤害,不是吗?
是的,如果第一个put需要扩展支持hashmap的数组
,那可能会受到影响如果在第一个put扩展hashmap时调用第二个put,则会发生错误和不可预测的结果。