2016-09-03 4 views
1

私はGuavaキャッシュの問題に直面しています。キャッシュに要素が1つしかない場合は、問題はありません。私は2番目の要素をロードするときには、そのログが言う古いキーを考慮したGuavaキャッシュ

private static LoadingCache<String, MyClass> cache = null; 
.... 
public MyClass method(final String id1, final long id2) { 
    log.error("inside with "+id1); 
    final String cacheKey = id1+"-"+id2; 
    if(cache == null){ 
     cache = CacheBuilder.newBuilder() 
     .maximumSize(1000) 
     .build(
      new CacheLoader<String, MyClass>() { 
       @Override 
       public MyClass load(String key) { 
        return getValue(cacheKey); 
       } 
      } 
     ); 
    } 
    try { 
     return cache.get(cacheKey); 
    } catch (ExecutionException ex) { 
     log.error("EEE missing entry",ex); 
    } 
} 

private MyClass getValue(String cacheKey){ 
    log.error("not from cache "+cacheKey); 
    ... 

} 

以前のエントリのキーを選択しようとしている:私は呼び出すと、例えばのために

inside with 129890038707408035563943963861595603358 
not from cache 1663659699-315839912047403113610285801857400882820 // This is key for the earlier entry 

方法を(「1」、2)、それはキャッシュの値をロードし、私はその後キャッシュからそれを得ることができます。今度はメソッド( "3"、4)を呼び出します。これはキャッシュにないので、getValue()が呼び出され、メソッド( "1"、2)のキーがログ出力されます

どこが間違っていますか?

+0

どのキー? –

+0

キャッシュを初期化する方法はスレッドセーフではないことに注意してください。さらに、それは非常に悪いアプローチのように聞こえる非静的メソッドに基づくCacheLoaderを使用する静的キャッシュであり、あまりにもエラーを起こしやすい。あなたのミスはおそらくそれらのものの1つによると思われます。 –

+0

このルール(http://stackoverflow.com/help/mcve)をお読みください –

答えて

3

あなたの問題は、CacheLoaderをどのように作成するかに関係しています。よく確認すれば、指定されたキャッシュキー(キャッシュが遅延初期化された時点のローカル変数cacheKeyの値)で初期化されますより汎用的で、keyCacheLoaderloadメソッドのパラメータとして指定する必要があります。それ以外の場合は、同じキーでgetValue(key)を呼び出してキャッシュをロードします。

それはこのようになります。

new CacheLoader<String, MyClass>() { 
    @Override 
    public MyClass load(String key) { 
     return getValue(key); // instead of return getValue(cacheKey); 
    } 
} 

NB:あなたのキャッシュは、それが初期化されていないし、あなたの方法methodがで呼び出された確かならば、安全なスレッドされていない初期化する方法複数のスレッドが同時に1つではなく複数回作成されます。

一つの方法は、次のようにダブルチェックロッキングイディオムを使用することができ

private static volatile LoadingCache<String, MyClass> cache = null; 
public MyClass method(final String id1, final long id2) { 
    ... 
    if(cache == null){ 
     synchronized (MyClass.class) { 
      if(cache == null){ 
       cache = ... 
      } 
     } 
    } 

NB:非静的に基づくCacheLoader静的キャッシュを初期化しないでください。メソッドは、あまりにもエラーが発生しやすいです。両方とも非スタティックまたはスタティックとしますが、それらを混用しないでください。

private static final LoadingCache<String, MyClass> cache = CacheBuilder.newBuilder()... 

も多くあなたのコードを簡素化するであろう遅延し、それを初期化する必要はありません:あなたは両方を静的にすることができ、あなたのキャッシュの初期化が非常に簡単に、それは簡単になりますと仮定すると

方法はそれだけに減らすように:

public MyClass method(final String id1, final long id2) { 
    log.error("inside with "+id1); 
    final String cacheKey = id1+"-"+id2; 
    try { 
     return cache.get(cacheKey); 
    } catch (ExecutionException ex) { 
     log.error("EEE missing entry",ex); 
    } 
} 
+0

は十分明確ですか?私が提案したものをテストしましたか? –

+2

また、 'Suppliers.memoize'がダブルチェックされたロックイディオムをカプセル化することを検討してください –

+0

これだけを今でもテストできました、ありがとう – rajesh

関連する問題