0

メソッドのアノテーションのルックアップをキャッシュするために 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 マップへのアクセスを同期する必要があるかどうかです。起こりうる最悪の事態は、並列の 2 つのスレッドが同じ (m, a) ペアをキャッシュ マップに入れることです。

私が最初に考えたのは、ConcurrentHashMap を使用することでしたが、null 値は許可されません (メソッドに注釈がない => null の場合に必要です)。Collections.synchronizedMap() を使用してマップへのすべてのアクセスを同期することも理想的ではありません。これは、この doSomethingWith() メソッドが非常に頻繁に呼び出されるためです。

この場合、HashMap へのアクセスを同期することが本当に必要なのでしょうか? キャッシュは実行時に変更されることはなく、キーと値のペアは一度だけ挿入され、削除されることはありませんが、何度も読み取られます。

何かご意見は?

4

2 に答える 2

3

マップが排他的に書き込まれる単一フェーズと、マップが排他的に読み取られる別のフェーズがある場合、同時コレクションは必要ありません。不変のマップ書き込み後フェーズでラップすることにより、マップが変更されないままであることを確認することもできます。

たとえば、Guava のImmutable mapを使用すると、次のようになります。

ImmutableMap.copyOf(map);


コレクションへの同時読み取り/書き込み/削除アクセスが予想される場合は、確実に ConcurrentHashMap を使用する必要があります。読み取り/書き込み/削除操作はアトミックではないため、かなり奇妙な結果になる可能性があります。


私が最初に考えたのは ConcurrentHashMap を使用することでしたが、null 値は許可されません (メソッドに注釈がない場合はここで必要です => null)。

最初に null 値を挿入しないでください。できれば、マップから既存のキーを削除してください。

于 2013-03-19T15:04:32.013 に答える