使用 java ConcurrentHashMap 实现缓存

发布于 2024-08-17 22:46:41 字数 178 浏览 2 评论 0原文

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

我是否缺少某些内容或 ConcurrentHashMap 方法(putIfAbsent,...)还不够,需要额外的同步?

是否有更好的简单 API(在内存存储中,无需外部配置)来执行此操作?

P。

I'd like to implement a simple caching of heavyweight objects in a web java application. But I can't figure out how to do it properly.

Am I missing something or ConcurrentHashMap methods (putIfAbsent, ...) are not enough and additional synchronization is needed ?

Is there a better simple API (In memory storage, no external config) to do this ?

P.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

一笑百媚生 2024-08-24 22:46:41

根据 Ken 的回答,如果创建一个稍后被丢弃的重量级对象是不可接受的(出于某种原因,您想保证为每个键只创建一个对象),那么您可以通过......实际上,不。不要自己做。使用 google-collections(现在 google-collections) google.com/p/guava-libraries/" rel="noreferrer">番石榴) 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) 就可以工作并完全删除你不必担心并发和同步的棘手问题。

请注意,如果您想添加一些更高级的功能,例如过期,那么

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

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

Further to Ken's answer, if creating a heavyweight object which later gets thrown away is NOT acceptable (you want to guarantee that only one object gets created for each key, for some reason), then you can do this by.... actually, don't. Don't do it yourself. Use the google-collections (now guava) MapMaker class:

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

Then a simple cache.get(key) just works and completely removes you from having to worry about tricky aspects of concurrency and syncrhonization.

Note that if you want to add some fancier features, like expiry, it's just

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

and you can also easily use soft or weak values for either keys or data if required (see the Javadoc for more details)

旧话新听 2024-08-24 22:46:41

如果可以安全地暂时为您尝试缓存的事物提供多个实例,则可以执行“无锁”缓存,如下所示:

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

多个线程可以“竞争”为该键创建和添加一项,但只有一个人应该“获胜”。

If it is safe to temporarily have more than one instance for the thing you're trying to cache, you can do a "lock-free" cache like this:

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

Multiple threads can "race" to create and add an item for the key, but only one should "win".

雄赳赳气昂昂 2024-08-24 22:46:41

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

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;

Instead of putting the "heavy objects" into the cache, you could use light factory objects to create an active cache.

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;
孤独陪着我 2024-08-24 22:46:41

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

变得简单多少

ConcurrentMap myCache = new ConcurrentHashMap();

不知道你能让Paul

ConcurrentHashMap should be sufficient for your needs putIfAbsent is thread safe.

Not sure how much simpler you can get

ConcurrentMap myCache = new ConcurrentHashMap();

Paul

看海 2024-08-24 22:46:41

我意识到这是一篇旧文章,但在 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());
    }
}

I realize this is an old post, but in java 8 this can be done without creating a potentially unused heavy object with a 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());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文