3

ConcurrentHashMap によって実装された、単純ですが非常に頻繁に使用されるキャッシュがあります。次に、定期的に (たとえば、15 分ごとに) すべての値を更新します。

次のようなコードが欲しいです:

 private void regularCacheCleanup() {
        final long now = System.currentTimeMillis();
        final long delta = now - cacheCleanupLastTime;
        if (delta < 0 || delta > 15 * 60 * 1000) {
            cacheCleanupLastTime = now;
            clearCache();
        }
  }

ただし、次のようにする必要があります。

  • スレッドセーフ
  • キャッシュがクリアされない場合、ノンブロッキングで非常にパフォーマンスが高い
  • java.* クラス以外の依存関係なし (Google CacheBuilder なし)
  • 堅実;-)
  • 新しいスレッドを開始できません

現在、ThreadLocal に短いタイマーを実装することを考えています。これが期限切れになると、リアルタイム タイマーが同期された方法でチェックされます。ただし、これは非常に多くのコードであるため、より単純なアイデアが適しています。

4

1 に答える 1

4

この問題に取り組む主流の方法は、タイマースレッドを使用して、指定された間隔でキャッシュを更新することです。ただし、新しいスレッドを作成する必要がないため、考えられる実装は、疑似タイミングのキャッシュ更新の実装です。基本的に、キャッシュアクセサー(putおよびgetメソッド)にチェックを挿入し、クライアントがこのメソッドを使用するたびに、putまたはgetアクションを実行する前にキャッシュを更新する必要があるかどうかをチェックします。これは大まかな考えです:

class YourCache {

  // holds the last time the cache has been refreshed in millis
  private volatile long lastRefreshDate;

  // indicates that cache is currently refreshing entries
  private volatile boolean cacheCurrentlyRefreshing;

  private Map cache = // Your concurrent map cache...

  public void put(Object key, Object element) {
    if (cacheNeedsRefresh()) {
      refresh();
    }
    map.put(key, element);
  }

  public Object get(Object key) {
    if (cacheNeedsRefresh()) {
      refresh();
    }
    return map.get(key);
  }

  private boolean cacheNeedsRefresh() {
    // make sure that cache is not currently being refreshed by some
    // other thread.
    if (cacheCurrentlyRefreshing) {
      return false;
    }
    return (now - lastRefreshDate) >= REFRESH_INTERVAL;
  } 

  private void refresh() {
    // make sure the cache did not start refreshing between cacheNeedsRefresh()
    // and refresh() by some other thread.
    if (cacheCurrentlyRefreshing) {
      return;
    }

    // signal to other threads that cache is currently being refreshed.
    cacheCurrentlyRefreshing = true;

    try {
      // refresh your cache contents here
    } finally {
       // set the lastRefreshDate and signal that cache has finished
       // refreshing to other threads.
       lastRefreshDate = System.currentTimeMillis();
       cahceCurrentlyRefreshing = false;
    }
  }
}

個人的にはそうすることは考えていませんが、タイマースレッドを作成したくない、または作成できない場合は、これが選択肢になる可能性があります。

この実装はロックを回避しますが、レースイベントのために更新が重複する傾向があることに注意してください。これで要件に問題がなければ、問題はありません。ただし、より厳しい要件がある場合は、スレッドを適切に同期してレースイベントを回避するために、ロックを設定する必要があります。

于 2012-09-14T12:10:38.843 に答える