使用Java ConcurrentHashMap实现缓存

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (121)

我想在web java应用程序中实现一个简单的重量级对象缓存。但我无法弄清楚如何正确地做到这一点。

我错过了什么或ConcurrentHashMap方法(putIfAbsent,...)是不够的,需要额外的同步?

有没有更好的简单API(在内存中,没有外部配置)来做到这一点?

P.

提问于
用户回答回答于

如果临时为您要缓存的对象暂时拥有多个实例是安全的,则可以像下面这样执行“lock-free”缓存:

public Heavy instance(Object key) {
  Heavy info = infoMap.get(key);
  if ( info == null ) {
    // It's OK to construct a Heavy that ends up not being used
    info = new Heavy(key);
    Heavy putByOtherThreadJustNow = infoMap.putIfAbsent(key, info);
    if ( putByOtherThreadJustNow != null ) {
      // Some other thread "won"
      info = putByOtherThreadJustNow;
    }
    else {
      // This thread was the winner
    }
  }
  return info;
}

多个线程可以“race”为键创建和添加项目,但只有一个应该“win”。

用户回答回答于

除了Ken的回答,如果创建一个重量级的对象,后来被扔掉是不可接受的(你想保证只有一个对象为每个键被创建,出于某种原因),那么你可以通过......来做到这一点。实际上,别。不要自己动手。使用google-collections MapMaker类:

Map<KeyType, HeavyData> cache = new MapMaker<KeyType, HeavyData>()
  .makeComputingMap(new Function<KeyType, HeavyData>() {
      public HeavyData apply(KeyType key) {
          return new HeavyData(key); // Guaranteed to be called ONCE for each key
      }
  });

然后,一个简单的cache.get(key)只是工作,并从不必担心并发性和syncrhonization的棘手方面完全删除你。

请注意,如果您想添加一些更新的功能,例如到期时间,这只是

Map<....> cache = new MapMaker<....>()
  .expiration(30, TimeUnit.MINUTES)
  .makeComputingMap(.....)

如果需要,还可以轻松使用软键或弱键值(请参阅Javadoc获取更多详细信息)

扫码关注云+社区