31

私はGuavaのLoadingCacheをプロジェクトに使用して、スレッド{safe、friendly}キャッシュの読み込みを処理していますが、これは非常にうまく機能します。ただし、制限があります。

キャッシュを定義する現在のコードは次のようになります。

cache = CacheBuilder.newBuilder().maximumSize(100L).build(new CacheLoader<K, V>()
{
    // load() method implemented here
}

有効期限は指定しません。

問題は、キーの値に応じて、関連付けられている値の一部が期限切れになる場合と期限切れにならない場合があることです。そしてCacheLoader、これを考慮していません。有効期限を指定すると、すべてのエントリに対して有効期限が設定されます。

この問題にどのように取り組みますか?

4

7 に答える 7

34

もう1つの方法は、 ExpiringMapです。これは、可変エントリの有効期限をサポートします。

Map<String, String> map = ExpiringMap.builder().variableExpiration().build();
map.put("foo", "bar", ExpirationPolicy.ACCESSED, 5, TimeUnit.MINUTES);
map.put("baz", "pez", ExpirationPolicy.CREATED, 10, TimeUnit.MINUTES);
于 2014-12-16T01:42:55.713 に答える
13

有効期限をエントリクラスに直接含め、キャッシュからフェッチした直後に有効期限が切れた場合は、手動でキャッシュから削除することをお勧めします。

MyItem item = cache.getIfPresent(key);
if (item != null && item.isExpired()) {
    cache.invalidate(key);
    item = cache.get(key);
    // or use cache.put if you load it externally
}

別の方法として、要素ごとの有効期限ポリシーをサポートするEhCacheライブラリを確認することをお勧めします。

于 2012-12-20T20:15:59.043 に答える
7

LoadingCacheいくつかの一般的に使用される有効期限ポリシーを提供しますが、これらが必要なものに満たない場合は、独自のポリシーを作成する必要があります。

DelayQueueを追加するだけです。キャッシュに何かを追加するときはいつでもDelayed、適切な有効期限でそのキューにを追加してください。オブジェクトには、キーへのDelayed(弱い?)参照が必要です。

最後の要素は、このキューを定期的にポーリングして、何かが期限切れになり、削除する必要があるかどうかを確認する必要があることです。これを行うために必ずしもスレッドを追加する必要はありません。にアクセスしているスレッドに便乗するだけで済みますLoadingCache。キャッシュにアクセスする直前。例:

private void drainCache() {
  MyDelayed expired;
  while ((expired = delayedQueue.poll()) != null) {
    K key = expired.getReference();
    if (key != null) { // this only in case if you hold the key in a weak reference
      loadingCache.invalidate(key);
    }
  }
}

..
V lookup(K key) {
  drainCache();
  return loadingCache.getUnchecked(key);
}
于 2013-01-14T01:23:12.930 に答える
0

明示的な無効化を使用して、削除するエントリを正確に定義できると思いますが、それはおそらくあなたが望むものではありません。

ただし、エントリに異なる重みを付けることができます。完璧ではありませんが、重要度の低いエントリを削除するようにキャッシュをガイドできます。ウェイトラーを参照してください。ウェイト0のエントリは、サイズベースの削除によって削除されません。

于 2012-12-20T20:04:20.733 に答える
0

エントリが大きく、メモリを節約する必要がある場合、良い解決策はないと思いますが、これらのハックが頭に浮かびます。

  • PriorityQueueエントリを手動で削除するには、有効期限順に並べられたものを使用します。期限切れのエントリが使用されないようにする場合は、これをhoazによるソリューションと組み合わせる必要があります。キューは、役に立たないエントリがメモリを占有するのを防ぐだけです。

  • 「関連する値の中には有効期限が切れているものとそうでないものがある」と書いたものです。これは、有効期限の遅延がすべての有効期限のあるエントリで同じであることを示しています。Queueこれにより、 (たとえば)ArrayDequeの代わりに、より簡単で高速な使用が可能になりPriorityQueueます。

  • 有効期限の遅延がかなり大きい場合は、すべてのエントリを期限切れにして、永久に存続するはずのエントリをに再挿入できますRemovalListener。これは2つの方法で失敗する可能性があります。1。その間にミスが発生する可能性があります。2.削除と再挿入には、多くのCPU時間がかかる場合があります。

于 2012-12-20T21:24:26.283 に答える
0

言及されたカフェインライブラリはこの機能を提供します。Expiryの独自の実装を提供する必要があります。有効期限は、キー、値、またはあなたが決定したものに依存する可能性があります。

    .expireAfter(new Expiry<CacheKey, CacheValue>() {
                        @Override
                        public long expireAfterCreate(@NonNull CacheKey key, @NonNull CacheValue value, long currentTime) {
                            return calculateBasedOnKey(key);
                        }
                        @Override
                        public long expireAfterUpdate(@NonNull CacheKey key, @NonNull CacheValue value, long currentTime, @NonNegative long currentDuration) {
                            return calculateBasedOnValue(value);
                        }    
                        @Override
                        public long expireAfterRead(@NonNull CacheKey key, @NonNull Long CacheValue, long currentTime, @NonNegative long currentDuration) {
                            return currentDuration; //not modifying the expiration time
                        }
于 2022-02-05T08:24:43.610 に答える
-1

グアバに触発されたカフェインライブラリを使用できます。これはgithubリポジトリからの使用例です

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterAccess(5, TimeUnit.MINUTES)

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterCreate(5, TimeUnit.MINUTES)

https://github.com/ben-manes/caffeine

于 2019-04-09T04:59:30.350 に答える