是否需要同步缓存注释信息的地图?

时间:2013-03-19 14:54:58

标签: java map annotations synchronization

我们正在使用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的访问?缓存必须在运行时更改,键/值对只插入一次从不删除但会被多次读取。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

如果您有一个单独的阶段,其中地图是专门写入的,然后是另一个阶段,其中地图是专门从中读取的,那么您不需要并发集合。 您还可以通过使用不可变的映射写后阶段将映射保存为未修改映射。

例如,使用Guava的Immutable map

ImmutableMap.copyOf(map);


如果您预见到对集合的并发读/写/删除访问权限,那么您肯定应该使用ConcurrentHashMap;因为读/写/删除操作不是原子操作,所以最终会得到一些非常奇怪的结果。


  

我的第一个想法是使用ConcurrentHashMap,但它不允许空值(如果方法没有注释,则需要此值=&gt; null)

然后首先不插入空值;更好的是,从地图中删除现有的密钥。

答案 1 :(得分:0)

  

现在的问题是我是否需要同步访问权限   ANNOTATION_CACHE地图与否。可能发生的最糟糕的事情是   并行的两个线程将相同的(m,a)对放入缓存映射中,   这不会伤害,不是吗?

是的,如果第一个put需要扩展支持hashmap的数组

,那可能会受到影响

如果在第一个put扩展hashmap时调用第二个put,则会发生错误和不可预测的结果。