java 缓存超时

hyp*_*man 3 java caching nonblocking

我们有一个简单但非常常用的缓存,由 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();
        }
  }
Run Code Online (Sandbox Code Playgroud)

除了它应该是:

  • 线程安全
  • 如果不清除缓存,则非阻塞且性能极高
  • 除了 java.* 类之外没有任何依赖项(因此没有 Google CacheBuilder)
  • 坚如磐石;-)
  • 无法启动新线程

现在我想在 ThreadLocal 中实现一个短定时器。当这个时间到期时,将以同步的方式检查真实计时器。然而,这是一个非常多的代码,所以一个更简单的想法会很好。

Lef*_*dis 5

解决此问题的主流方法是使用一些计时器线程按指定的时间间隔刷新缓存。但是,由于您不需要创建新线程,因此我能想到的一种可能的实现是伪定时缓存刷新。基本上,我会在缓存访问器(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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

就我个人而言,我不会考虑这样做,但如果您不想或无法创建计时器线程,那么这可能是您的一个选择。

请注意,尽管此实现避免了锁定,但由于竞争事件,它仍然容易出现重复刷新。如果这可以满足您的要求,那么应该没有问题。但是,如果您有更严格的要求,那么您需要设置锁定,以便正确同步线程并避免竞争事件。