简体   繁体   中英

Does a map that caches annotation info need to be synchronized?

We are using a HashMap to cache the lookup of annotations on methods. The annotations are retrieved with Spring's AnnotationUtils.findAnnotation . Not using the cache results in a drastic performance hit.

Our implementation looks somehow like:

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
            }
        }
    }
}

The question now is if I need to synchronize the access to the ANNOTATION_CACHE Map or not. The worst thing that could happen is that two threads in parallel put the same (m, a) pair into the cache map, which would not hurt, isn't it?

My first thought was to use the ConcurrentHashMap, but it does not allow null values (which is needed here if a method has no annotation => null). Using Collections.synchronizedMap() and synchronizing every access to the map also is not ideal, because this doSomethingWith() method is called very frequently.

So is it really necessary to synchronize the access to the HashMap in this case? The cache nevery changes at runtime and key/value pairs get inserted only once get never removed but will be read many times.

Any thoughts?

If you have a single phase where the map is exclusively written to and then a different phase where the map is exclusively read from, then you don't need a concurrent collection. You could also make sure that the map remains unmodified by wrapping it with an immutable map post-write phase.

For example, using Guava's Immutable map :

ImmutableMap.copyOf(map);


If you foresee concurrent read/write/remove access to the collection, then you definitely should use ConcurrentHashMap; because read/write/remove operations are not atomic, you could end up with some pretty strange results.


My first thought was to use the ConcurrentHashMap, but it does not allow null values (which is needed here if a method has no annotation => null)

Then don't insert the null value in the first place; better yet, remove the existing key from the map.

The question now is if I need to synchronize the access to the ANNOTATION_CACHE Map or not. The worst thing that could happen is that two threads in parallel put the same (m, a) pair into the cache map, which would not hurt, isn't it?

Yes that can hurt if the first put need to expand the array backing the hashmap

If the second put is called while the first put expands the hashmap, bad and unpredictable results can happend.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM