17

我想在 web java 应用程序中实现重量级对象的简单缓存。但我不知道如何正确地做到这一点。

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

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

P。

4

5 回答 5

25

根据 Ken 的回答,如果创建一个后来被丢弃的重量级对象是不可接受的(出于某种原因,您想保证每个键只创建一个对象),那么您可以通过......实际上,不。不要自己做。使用google-collections(现在guavaMapMaker 类

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)可以完全消除您不必担心并发和同步的棘手方面。

请注意,如果您想添加一些更高级的功能,例如到期,它只是

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

如果需要,您还可以轻松地为键或数据使用软或弱值(有关更多详细信息,请参阅 Javadoc)

于 2010-01-15T20:34:59.063 回答
14

如果为您要缓存的东西暂时拥有多个实例是安全的,您可以像这样执行“无锁”缓存:

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;
}

多个线程可以“竞相”为密钥创建和添加项目,但只有一个线程应该“获胜”。

于 2010-01-15T09:28:18.630 回答
2

您可以使用轻工厂对象来创建活动缓存,而不是将“重对象”放入缓存。

public abstract class LazyFactory implements Serializable {

  private Object _heavyObject;

  public getObject() {
    if (_heavyObject != null) return _heavyObject;
    synchronized {
      if (_heavyObject == null) _heavyObject = create();
    }
    return _heavyObject;
  }

  protected synchronized abstract Object create();
}

// here's some sample code

// create the factory, ignore negligible overhead for object creation
LazyFactory factory = new LazyFactory() {
  protected Object create() {
    // do heavy init here
    return new DbConnection();
  };
};
LazyFactory prev = map.pufIfAbsent("db", factory);
// use previous factory if available
return prev != null ? prev.getObject() : factory.getObject;
于 2010-01-15T09:21:58.667 回答
1

ConcurrentHashMap 应该足以满足您的需求 putIfAbsent 是线程安全的。

不知道你能得到多少简单

ConcurrentMap myCache = new ConcurrentHashMap();

保罗

于 2010-01-15T09:09:19.957 回答
0

我意识到这是一篇旧文章,但在 java 8 中,这可以在不使用 ConcurrentHashMap 创建可能未使用的重对象的情况下完成。

public class ConcurrentCache4<K,V> {
    public static class HeavyObject
    {
    }

    private ConcurrentHashMap<String, HeavyObject> cache = new ConcurrentHashMap<>();

    public HeavyObject get(String key)
    {
        HeavyObject heavyObject = cache.get(key);
        if (heavyObject != null) {
            return heavyObject;
        }

        return cache.computeIfAbsent(key, k -> new HeavyObject());
    }
}
于 2017-10-06T07:09:54.897 回答