java缓存超时

时间:2012-09-14 11:21:53

标签: 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();
        }
  }

除了它应该是:

  • 线程安全
  • 如果不清除缓存,则非阻塞且性能极高
  • 除了java。*类之外没有依赖关系(所以没有Google CacheBuilder)
  • 坚如磐石; - )
  • 无法启动新主题

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

1 个答案:

答案 0 :(得分:3)

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

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

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