7

効率的な方法で、キャッシュに存在しない値を同時に何度もロードするのを防ぐ方法は?

一般的なキャッシュの使用法は、次の疑似コードです。

Object get(Object key) {
 Object value = cache.get(key);
 if (value == null) {
  value = loadFromService(key);
  cache.set(key,value);
 }
 return value;
}

問題: 値がサービス (データベース、WebService、RemoteEJB など) からロードされる前に、2 回目の呼び出しが同時に行われる可能性があり、これにより値が再度ロードされます。

たとえば、ユーザー X のすべてのアイテムをキャッシュしていて、このユーザーが頻繁に表示され、多くのアイテムを持っている場合、すべてのアイテムの負荷を同時に呼び出す可能性が高く、サーバーに大きな負荷がかかります。

get関数をsynchronizedにすることもできましたが、これにより他の検索が待たされることになり、あまり意味がありません。すべてのキーに対して新しいロックを作成することはできますが、 Javaでこのような多数のロックを管理するのが良い考えかどうかはわかりません(この部分は言語固有であり、タグを付けた理由ですjava)。

または、私が使用できる別のアプローチがありますか?もしそうなら、何が最も効率的でしょうか?

4

3 に答える 3

7

一般的にできることは、オブジェクトの hashCode を使用することです。

衝突の可能性を減らすために、hashCode に基づいて使用されるロックの配列を持つことができます。またはハックとして、自動ボックス化されたバイトが常に同じオブジェクトを返すという事実を利用できます。

Object get(Object key) {
    Object value = cache.get(key);
    if (value == null) {
        // every possible Byte is cached by the JLS.
        Byte b = Byte.valueOf((byte) key.hashCode());
        synchronized (b) {
            value = cache.get(key);
            if (value == null) {
                value = loadFromService(key);
                cache.set(key, value);
            }
        }
    }
    return value;
}
于 2013-01-23T10:15:53.310 に答える
3

車輪を再発明しないでください。グアバLoadingCacheまたはメモ化サプライヤーを使用してください。

Ehcache を使用している場合は、read-throughについて読んでください。これが求めているパターンです。CacheEntryFactoryキャッシュ ミス時にオブジェクトを読み取る方法をキャッシュに指示するインターフェイスを実装する必要があり、Ehcacheインスタンスを のインスタンスでラップする必要がありますSelfPopulatingCache

于 2013-01-23T10:24:54.527 に答える
1

読み込み時に、結果の代わりに中間オブジェクトをマップに挿入して、読み込みが開始されたが終了していないことを示します。以下の java.util.concurrent.FutureTask は、中間オブジェクトに使用されます。

Object get(final Object key) throws Exception {
    boolean doRun = false;
    Object value;
    synchronized (cache) {
        value = cache.get(key);
        if (value == null) {
            value = new FutureTask(new Callable() {
                @Override
                public Object call() throws Exception {
                    Object loadedValue = loadFromService(key);
                    synchronized (cache) {cache.put(key, loadedValue);};
                    return loadedValue;
                }

            });
            cache.put(key, value);
            doRun=true;
        }
    }
    if (value instanceof FutureTask) {
        FutureTask task = (FutureTask) value;
        if (doRun) {
            task.run();
        }
        return task.get();
    }
    return value;
}`
于 2013-01-23T11:16:44.900 に答える